Text
                    Справочник программиста персональн~.1х компьютеров типа
IBM РС,
ХТиАТ


Р.джордеин СПРАВОЧНИК программиста персональных компьютеров типа IBM РС; ХТиАТ Перевод с ан гл и йского Н.В.Гай<;кого · МОСКВА "ФИНАНСЫ И СТАТИСТИКА" 1992
Programmer's ProЫem Solver for the IBM Ре, ХТ&АТ ROBERT JOURDAIN BRAPY. NewYork
ББК 24.4.1 Дж40 Джордейн Р. Дж40 Справочник программиста персональных компьютеров 1 типа IВМ РС, ХТ и АТ: Пер. с. англ./ Цредисл. Н. В. • ГаJского. -М.: финансы и статистика, 199Z: - 544 с.: ил. ISBN 5-279-00611-4. Универсальный справочник пользователя и программиста IВМ РС - сов­ местимого персонального компьютера сводит воедино информацию, рассре­ доточенную в документации, руководствах, учебных пособиях. Свойства полноты н самодостаточности делают ero ценным источником для широкого круга профессиональных пользователей персональных компьютеров. д2404010000 -006 141 - 91 010(01) - 92 • ISBN 0-89303 -787-7 (США) ISBN 5-279-00611-4 (CCCP)t ББК 24.4.1 © 1986 Ьу Brady Books, а divi- sion of S1rt10n & Schuster Inc. <С> Н.В.Гайский, перевод и прt­ дисловие, 1991
Предисловие к русском у изданию В оригинальном на;ё~вании предлагаемой советскому 'читателю книги .Роберта Джордейна "Programmer·s proЬlem solver" ключевым является слово "solver", переводимое как сборник решений. И действительно, в ней собраны решения наиболее типичных задач, с которыми сталкивается программист при написании программ. Но, как пишет во введении автор, основная цель книги - с_обрать воедино и систематизировать огромное количество информации по программированию· на персональном компьютере типа IВМ РС.. Ценность! книги особенно велика для советского программиста, поскольку далеко не· каждый может легко получить доступ к документации фирм-изготовителей вспомогательных микросхем. Но даже при наличии документации вы не всегда можете понять, как решить стоящую перед вами проблему средствами языка высокого уровня, а ведь подавляющее. большинство программ пишется на этих языках. Указанная книга поможет вам разобраться и в такой пробл~ме. Хотя в качестве языка высокого уровня, выбран. Бейсик, котоJjый не слишком распространен в Советском Союзе, тем не менее вы усвоите главное: можно ли реализовать нужную вам функцию в рамках языf(а высокого уровня или необходимо включать в свою программу ассемблерные подпрограммы. Хотелось бы отметить и удачный способ расположения 1 .., / ' 1 ' материала: каждая глава посвящена ,устроиству или группе близких устройств и описаны все типичные проблемы, возникающие при работе с ними. Несомненно, такой порядок • намного более удобен, чем упорядочение по номеру функции операционной системы или алфавиту. Но надо отметить, что в ряде случаев описаны устройства, с которыми советскому программисту вряд ли придется столкнуться в своей работе, например световым пером. С другой стороны, в книге нет информации об устройствах, появившихся за последние нес~олько лет,_ в частности, получившем широкое распространение VGА­ дисплее. Это связано с тем, что американское издание -было выпущено до появления дисплеев такого типа. Однако мелкие недостатки книги _полностью компенсируются ее достоинствами. Во-первых, •вы получаете униве~альный справочник, охватывающий практически все компоненты и устройства персонального компьютера. Во-вторых, многочисленные
примеры показывают вам путь, по которому программист должен двигаться при решении конкретной задачи. И хотя ни один из примеров не является самостоятельно работающей программой, ценность • их несомненна. Как правило, в них отмечены все пустяки и мелочи, уже известные опытному программисту, но которые могут привести к огромным затратам времени и сил, если ранее вы не сталкивались с аналоmчными проблемами. Суммируя вышесказанное, можно выделить две основные группы читателей, для которых книга Джордейна будет особенно полезна. Это те, кто •постоянно программирует на персональном компьютере и воспользуется книгой в качестве справочника. И те, кто захотел понять, как работают программы, и для этого выбрал лучший способ - попробовал •программировать сам. В заключение хотелось бы дать совет всем, кто начнет, изучение программирования с этой книm: не бойтесь ошибиться, экспериментируйте, и вы будете вознаграждены за свои старания пониманием того, как работает ваш персональный компьютер. •Н.В.Гайский
Введение В наше· время программисты являются одной из наиболее нова­ торских групп. К сожалению их наиболее удачные новшества включают и несколько новых способов потери .времени. Беско­ нечны ужасные истории о программах, для отладки которых требуется в двадцать раз больше времени, чем для написания. И вы можете снова и снова слышать о программах, к которым приходится обращаться вновь и вновь, так как они не были достаточно хорошо продуманы с самого начала. Намного меньше сказано о том, что может оказаться самым емким способом пустой траты времени для изучающих программирование: поиск информации о машине. Многочасовые усилия по установлению простого факта являются настоящим обрядом посвящения для начинающих программистов, которые должны рыться в руководствах до потемнения в глазах. Типичное следующее утро после этого: глаза, слезящиеся от терминала, метровая стопа смятых распечаток и пара дюжин руко- •водств, рассыпанных по всему полу. Среди них руководства по оборудованию, по операционной системе MS-DOS, по языку прог­ раммирования, а также описания. отдельных микросхем, печ.;1та­ ющего устройства и клавиатуры плюс дюжина дополнительных книг, каждая из которых содержит бесценный кусочек инфор­ мации, понадобившийся в три часа ночи для особо тонкого места в программе. Поскольку немногие обладают фотографической памятью (а работа с компьютерами может лишить в.tс остатков памяти), все эти книги действительно необходимы, т;ж как одни и те же. вещи приходится искать снова и снова. На первых порах вы можете· затрачивать часы и не находить требуемой информации. Даже -.~ели вы обнаружили нужное место, то может понадобиться доста­ ·точно много времени, чтобы вытянуть искомое из пространного описания для начинающих, или, если, к вашему несчастью, руко­ водство написано на языке суахили, то не меньше чем полдня уйдет на перевод. Хотелось бы иметь одну большую книгу, в кото­ рой собрано практически все необходимое, не разбавленное инфор- , мацией, ненужной для программистов, написанную на среднем уровне сложности, описывающую все компоненты IBM РС и орга.:. низованную таким способом, чтобы в ней легко было отыскать необходимую информацию. Но слышали ли вы когда-нибудь о ,такой .книге? •
8 Введение Я собрал вместе все эти руководства и описания для тех, кто хочет научиться писать нетривиальные программы, но не может позволить себе тратить массу времени (или 600 - 800 дол., чтобы купить все эти книги)_. Материал представлен двумя способами. Во-первых, главJ>I делятся по типу описываемого оборудования, их разделы относятся к •определенному свойству данного оборудования и включают короткие параграфы, относящиеся к конкретной программистской задаче. Например, один из разделов главы, посвященной выводу на дисплей, относится к курсору » содержит параграфы, описывающие, как позиционировать курсор, менять его форму, включать и выключать его и т.д. Во-вторых, каждый, параграф делится на четыре части (иногда меньше). Сначала идут несколько абзацев, описывающих основные понятия. Затем рассматриваемая задача обсуждается с точки зре­ ния программирования на языках высокого уровня, среЩtего уровня (прерываний BIOS и DOS) и низкого уровня вспомогатель­ ных микросхем, поддерживающих микропроцессор. Кроме того, каждый из разделов начинается с пары страниц, содержащих све­ дения, необходимые для понимания данного раздела. Эти сведения носят _характер обзора и вы можете использовать их, чтобы наметить свой путь изучения книги при первом ее просмотре. Обсуждение программирования на языке высокого уровня поможет читателю решать конкретные проблемы. Хотя рассматри­ ваемые концепции в равной степени применимы и к Паскалю, и к Си, все примеры приведены на Бейсике. Бейсик выбран отчасти потому, что он является базовым языком для микроЭВМ, отчасти потому, что каждому владельцу IВМ РС он доступен, и отчасти потому, что Бейсик фирмы Microsoft по сравнению с другими язы­ ками программирования предоставляет наиболее. полные средства для использов~ния возможностей оборудования IВМ РС. Даже начинающие программировать на Бейсике найдут в книге много полезного. Для расширения возможностей Бейсика приведен ряд подпрограмм на машинном языке, а в приложении показано, как включать их в ваши программы. С помощью этих подпрограмм вы можете делать такие тонкие вещи, как перепре,граммиров_ание клавиатуры или создание дополнительных дисплейных странцц для монохромного адаптера. Решение проблем с помощью прерываний, включенных в опе­ рационную систему, будем называть программированием на сред­ нем уровне. IJрерывания - это мощные компактные процедуры, выполняющие нудную работу любого компьютера, такую; как перемещение курсора или чтение каталога диска'. Это область программистов на языке ассемблера, и все примеры программиро­ вания на среднем уровне приведены на языке ассемблера. Но теперь все больше и больше трансляторов с языков высокого уровня предоставляют доступ к прерываниям, позволяя грамот-
9 ному программисту проделывать операции, которые не позвоJtЯет сам язык, например чтение абсолютного сектора диска. Цоэтому материал,' относящийся к программированию на среднем уровне, представляет больший интерес, чем может показаться н,а первый взгляд. Все обсуждения относятся только к опеуационной системе MS-DOS. Если вы работаете в системе СР / М-86 или UCSD p-system, то вам придется поискать другое руководство. Наконец, примеры, связанные с программированием на низком уровне, показывают, как даJiная проблема может быть решена программированием микросхем. Rce микроЭВМ, совместимые с IВМ РС, имеют одну и ту же архитектуру, поскольку их основой являются микросхемы фирмы Iпtel. Доступ к микросхемам осу­ ществляется через порты ввода/вывода, к которым вы имеете дос­ туп, работая практически с любым языком, включая Бейсик. Обсуждаются все важные для программиста микросхемы, включая таймер, интерфейс с периферией, контроллер прерываний, конт­ роллер дисплея, контроллер НГМД (накопителя на гибких магнит­ ных дисках) и микросхемы управления коммуникационным • каналом. Хотя фирма IВМ не рекомендует программировать на этом уровне (из соображений, что такая программа может не рабо­ тать на последующих модификациях ЭВМ), снова и снова обнару­ живаются возможност'и машины, которые нельзя реализовать друmм способом. 1 Не все задачи показаны на всех трех уровнях. Решение неко­ торых из них просто невозможно на Б~йсике. Для решения других не предусмотрено средств операционной системы. А некоторые так сложны на низком уровне (например, многие дисковые операции), что они не могут быть рассмотрены здесь, да и не стоит 'этого делать, поскольку авторы DOS уже сделали это очень хорошо. Однако в большинстве случаев показаны все три,, уровня. Срав­ нивая различные уровни между собой, вы можете увиде-rь, как спуститься от языков высокого уровня к прерываниям и, , в свою очередь, как прерывания работают с микросхемами, явлstющимися сердцем компьютера. Эта книга может показаться ужасной,лю.цям, знакомым только с языками высокого уровня, такими, как Бейсик или Паскаль. Это является следствием того, что разделы, относящиеся к программи­ рованию на среднем и низком уровнях, написаны на языке ассем­ блера. Эта книга послужит идеальным компаньоном для тех, кто изучает ассемблер. Но не думайте, что вам нужна только треть книги, если вы не знаете ассемблера и 'не собираетесь изучать его. Во-первых, ряд трансляторов, таких, как TurЬo Pascal или Lattice С, позволяют вам использовать функции операционной _системы, показанные на среднем уровне. Кроме того, мноmе из процедур низкого уровня могут быть на самом деле реализовцны на языках высокого уровня. Чтобы вы могли разобраться, что же содержится
10 Введение в приведенных примерах на ассемблере, в приложении Г дано краткое введение в этот язык. Даже если вы никогда не будете использовать материал, связанный с программированием на низ­ ком уровне, внимательный взгляд на него позволит вам намного глубже понять, как же работают'языки высокого уровня и почему в некоторых случаях возникают затруднения при работе с ними. Практически каждый параграф содержит образец кода. Часто это всего лишь несколько тривиальных строк. Иногда приводятся явные наметки для реализации сложных процедур. Очень редко встречаются самостоятельные · программы. Вместо того чтобы заполнять книгу изощренными примерами, я в большинстве слу­ чаев оставлял лишь фра,гмент кода, который понадобится вам, когда вы обратитесь к этой книге за помощью. Приведенные при­ меры ни в коей мере не претендуют на С.Jмое красивое решение проблемы. Основная цель примеров состоит не в том, чтобы пре­ доставить набор готовых программных модулей, а в том, чтобы указать путь решения возникающих проблем, чтобы вы могли начать думать в правильном направлении. Но если вы хотите, то можете прямо включать приведенные образцы в свои программы в качестве функциональной отправной точки и затем дорабатывать . их до кондиций, удовлетворяющих вашему эстетическому вкусу. Поскольку все примеры проверены, вы можете спокойно пользо­ ваться ими, чтобы избежать действительно идиотских ошибок, которые имеют тенденцию накапливаться после того, к~tк долгие часы про~;раммирования понизят ваш интеллект практически до нулевого IQ. Язык этой книги, мягко говоря, очень компактный. Но я ста­ рался не прибегать к жаргону, насколько это возможно. Кроме того, в кенце кюt:ги приведен терминологический словарь компью­ терных терминов. За исключением материала весьма специального свойства, практически вся относящаяся к программированию информация, доступная из документации IВМ, включена в книгу. Хотя, конечно, было бы прекрасно охватить все, но тогда объем книги достиг бы 1ООО страниц и за деревьями вы не увидели бы леса. Поэтому для необычных программистских нужд - скажем, для сложных программ управления контроллером НГМД или пере­ программирования клавиатуры АТ - вам придется обращаться к. техническим руководствам IBM .или специальным описаниям, выпускаемым производителями микросхем. Но 99% программ не потребуют другой информации об оборудовании IВМ РС, кроме содержащейся в данной книге. Различные способы решения той или иной проблемы собраны в одном месте, и приводится срав­ нение сильных и слабых сторон того или иногq подхода. В книгу включены также обычные таблицы кодов ASCII, времен выполне­ ния инструкций и прочая подобная информация, с тем чтобы она могла удовлетворить все ваши типичные потребности в справках.
11 Имеется также много информации, которая опущена в доку­ ментации IBM, например, какие управляющие коды интерпретиру­ ются какими программами вывода на экран или как различные дисковые функции работают с файлами. В некоторых параграфах показано решение типичных задач программирования, не связан­ ных напрямую с оборудованием, но использующих некоторую его специфику, таких, как работа в реальном времени или горизон­ тальная прокрутка. Уделено также место и программным трюкам, которые если и не вызываются вf,Iсшими силами, то вполне дос­ тойны того, чтобы программист знал о них. При существующем положении вещей каждый программист должен открывать эти методы для себя (причем обычно не один раз). Как печально, что высшие жрецы Века Информации тратят так много времени, пере­ изобретая колесо, как в давние времена, когда папирус еще не сделал обмен информацией достаточно легким. Приводится также информация об отличиях разных версий IВМ РС. Все обсуждения относятся к стандартному IВМ РС. В тех случаях, когда PCjr, ХТ или АТ ведут себя по-разному, описы­ ваются отличительные свойства данной машины. Попутно сразу отметим, что в книге совершенно не рассматриваются сетевые возможности, появившиеся у АТ и в MS-DOS 3.0. Эти вопросы заслуживают отдельноi-t книги. За некоторым исключением, все образцы кода рассчитаны на стандартный IВМ РС, но пока не ска­ зано обратное, все они будут нормально работать на любом из подвидов. Однако есть существенное ограничение. Все написанное в этой книге предполагает применение MS-DOS 2.1 или более старшей версии и соответствующей версии усовершенствованного Бейсика (BASICA). Пользователям, до сих пор не перешедшим на MS-DOS 2.1, недоступны мнqгие достоинства машины. Если в этой книге что-то и содержится, то это факты - мириады их, и я искренне надеюсь, что все они верны. В нее •включено также несколько сотен примеров программ, и я готов поклясться, что они совершенны. Но если вы думаете, что такое огромное количество информации можно оставить неповрежденным в длительном •процессе -подготовки книги к изданию, то попробуйте. Если вы обнаружите что-нибудь ужасное, вздохните глубже и подумайте о том, насколько хуже была бы ваша жизнь, если бы этой книги не было. После этого сядьте и напишите мне письмо по адресу: Brady Со., Simon & Schuster, General Reference Group, 1230 Avenue of the Americas, New York, NY 10020. Если вы сделаете это, то жизнь станет немного лучше для тех программистов, которые ·получат второе издание книги, дополненное сведениями о последних созданиях IBM. Удачного программирования! Роберт Джордейн
12 Соrлашення о числах, принятые в этой кннrе Прогр~ммисты на ассемблере не найдут ничеrо необычноrо в способе предс,:авления чисел и адресов, принятом в .этой книге. Но многие программисты, работающие с языками высокоrо уровня, мало знакомы с системой адресации и недесятичными числами, и они могут быть слегка сконфужены на первых порах. Если вы относитесь к этой катеrории, не отчаивайтесь! Данная ·книга сравнительно безболезненно познакомит вас с этой кабалистикой, а ваше образование ка~ программиста будет существенно ограничено без знакомства с этими вещами. Чтобы помочь вам, в книгу включены два приложения. В ·приложении А обсуждаются двоичные и шестнадцатиричные числа, а также их использование при адресации памяти. В приложении Б более подробно разбираются двоичные числа и- их использование в битовых операциях. Даже если вы не нуждаетесь в помощи, обратите внимание на следующие правила: 1 1. Для ущ,бства менее классных программистов все числа считаются десятичными до тех пор, пока за ними не следует Н (для шестнадцатиричных) или В (для двоичных). Иногда В опускается после двоичных чисел, когда очевидно, что их значения описывают цепочку битов. 2. Друrое исключение - ч11сла из восьми цифр вида 0000:0000. Это шестнадцатиричные числа, дающие сегмент II смещение адреса памяти. Их значение объяснейо в приложении А. 3.БитынумеруютсяотОдо7(илиотОдо15),гдебитО соответствует младшему биту (т.е. когда установлен бит О =· 1 и бит 7 = 128). 4. Выражение вида "ASCII 5" относится к символу номер 5 набора ASCII. Это означает, что оно относится к одному байту со значением 5, а не к коду ASCII для символа 5 и не к двухбайтному целому, представляющему значение 5. 5. Числа, заключенные в квадратные скобки, например [2.1.3 ], являются перекрестными ссылками на другие разделы данной . книги. Приведенный прим-=:р подразумевает "глава 2, раздел 1, параграф 3". [2.1 .0] относится к общему· обсуждению, начинающему раздел 1 гл. 2. Вы обнаружите сотни таких ссылок, рассеянных по всему тексту. Он11 отсылают вас к тем местам книги, где можно найти нужную информацию. Их основное назначение - помощь начинающим. Если вы понимаете, о чем идет речь, игнорируйте перекрестные ссылки.
Глава 1. СИСТЕМНЫЕ РЕСУРСЫ 1.1 . Ревнзня- снстемных ресурсов Одной из первых пр~блем, возникающих после загрузки задачи, является проверка, куда мы "попали": на каком типе IВМ РС запущена задача'? ... под какой версией MS- DOS? ... каким объемом памяти мы располагаем'?... все ли необходимое оборудование имеется'? Существуют три способа получения этой •информации. Наименее элегантный из них - спросить об этом у пользователя (но знает ли он, что ответить'?). Намного лучше получить всю . доступную информацию из установки переключателей на системной плате. Но эта установка не всегда соответствует реальности. Поэтому лучше всего получить прямой доступ к требуемому оборудованию или прочитать нужную информацию из области данных BIOS. Поскольку установка . переключателей может служить отправной точкой для получени~ необходимой информации, то этот раздел начинается с обсуждения микросхемы, содержащей такую информацию, - микросхемы интерфейса с периферией 8255. Программа может получить доступ к оборудованию только двумя способами. Она может обратиться к любому из портов ввода/вывода, соответствующему присоединенному оборудованию (обычно занята лишь малая доля из 65535 возможных адресов портов), или к любому из более чем миллиона адресов оперативной памяти. Сводная таблиl..(8 адресов портов приведена в [7 .3 .0 ]. На рис. 1.1 показано 1 как распределены в памяти операционная система и программы.
14 ·Мегабайтовое адресное пространство микропроцессора 8088 00000 00400 АОООО 80000 88000 соооо С8000 FбООО PSP Таблица векторов прерываний Область данных BIOS Драйверы устройств DOS 1} Резидентные программы Ваша программа Файлы данных, используемые программой Глава 1 Видеобуфер монохромного диспnеА Видеобуфер цветного диспnеА Максимальный видеобуфер EGA • BIOSEGA ПЗУ фиксированного диска Бейсик-ПЗУ ROM-8l0S Рис. 1.1 . Использование мсгабайтового адресн,ого 11ространсп~а 1.1.1. Доступ к мнкросхеме ннтерфенса с пернфернен 8155 Микросхема интерфейса с периферией Intel 8255 - лучшее место, с которого надо начинать, чтобы полv•1и-f1, информацию об имеющемся 'оборудовании. Эта микросхема предназначена для многих целей. Она сообщает об установ·ке переключателей на
Системные ресурсы 15 системной плате, принимает для компьютера ввод с клавиатуры, управляет рядом периферийных устройств, включая микросхему таймера 8253. Из м3шин семейства IВМ РС только АТ не исцользует микросхему 8255; он хра'нит информацию об оборудовании вместе с часами реального времени в специальной микросхеме с независимым питанием. Однако для работы с r<лавиатурой и управления микросхемой таймера микросхема АТ использует те _же адреса портов, что и 8255. . Микросхема 8255 имеет три однобайтовых регистра, называемых от порта А до порта С. Адреса этих портов от бОН до 62Н соответственно. Считывать данные можно из всех трех портов, но писать можно только в порт В. Для РС установка бита 7 порта В в 1 изменяет информацию, содержащуюся в порте А. Аналогично для РС установка бита 2 определяет содержимое четырех младших битов порта С, а установка бита З делает то же самое для ХТ. Содержимое этих регистров следующее: Порт А (60Н) когдаu11ортсВбит7=О бит1,1 0-7 PC,X 'J .:,PCjr,AT: 8-бнто111,1с скан-коды с кла~i~штуры коI','.Щ 11· ~юрте В б~п 7 = 1 д;I11 РС бит о 1 2-3 4-5 6-7 Порт В (61Н) бит о 1 2 3 4 5 6 5-6 РС: О = нет ш1к011~пслсй на i1искетах РС: нс ис11ользуется РС: число банко11 11амяти ш~' с~1стсм1юй нлатс РС: тип дисплея (11 = монохромный, 10 = цнетной 80*25, 01 = цветной 40*25) РС: •шсло накопителей на дискетах PC,XT,PCjr: унраuляст каналом 2 таймера 8253 rc,xт,PCjr: UЫIIOД Ш\ ДИIШМНК РС: 11ыбор содсржнмшх, норта С PCjr: 1 = симuолы1ый режим, О = 1-раф~1ческий PC,!'Cjr: 1 = кассеп1ый мотор ш,Iклю•Iс11 ХТ: uыбор содержимо1u ~юрта С РС,ХТ: О = раз~ше1ше ОЗУ PCjr: 1 =, запрет динамика и мотора кассеты РС,ХТ: О = разреше1шс ош~1бок щелей рас1ш1рсш1я РС,ХТ: 1 = рнзрешеш1с часо11 кла11натуры РСjг: 11ыбор. дшшмика (00 = 8253, 01 = кассета, 1О = шюд/11ы1юд, 1J = м~Iкросхсма 76496>
16 7 РС:- выбор содержимого порта А РС,ХТ: подтвержде11ие клав~~атурь1 Глава 1 Порт С (62Ю когдавпортеВбит2=1дляРСилибит3=1Д.1111 ХТ биты 0-3 РС: нижняя половина переклю<1ателя 2 конфи­ rурацш1 (ОЗУ на плате расширения) О PCjr: 1 = введенный символ потерян ХТ: 1 = есть мат. сопроцессор PCjr: с.сть карта модема 2 PCjr: есть карта НГМД 2-3 ХТ: число банков памяти на системной плате 3 PCjr: О = 128К памяти 4 PC,PCjr: ввод с кассеты ХТ: не используется 5 PC,XT,PCjr: выход канала 2 8253 6 РС,ХТ: 1 = проверка ош~tбок щс;1ей расширения PCjr: 1 = данные с клавиатуры 7 РС,ХТ: 1 = контроль 01~ибок четности PCjr: О = кабель клавиатуры подсоедине!" когда_впортеВбит2=ОдJJЯРСилибит3=ОдJIЯХТ' биты 0-3 РС: верхняя половина переклю•~ателя 2 конфи~ rурации (не используется) 0-1 ХТ: тип дисплея (1 1 = монохромный, 10 = цветной 80*25, 01 = цветной 40•25) 2-3 ХТ: число накопителей НГМД (00 = 1 и т.д.) 4-7 РС,ХТ: то же, что и с устаноw~енными бит~1ми Отметим, что О в одном из битов регистра соответствует установке переключателя "off". АТ хранит информацию о конфигурации в микросхеме МС146818 фирмы Motorola вместе с часами реального времени. Он вовсе не имеет микросхемы , 8255, хотя для управления микросхемой таймера и приема данных. с клавиатуры используются те же адреса портов. Микросхема имеет 64 регистра, пронумерованных от 00 до ЗFН. Для· чтения ре~стра нужно сначала послать .ero номер в порт с адресом 70Н, а · затем прочитать ero через порт , 71Н. Различные параметры конфигурации обсуждаются далее. Приведем здесь только краткую сводку:
Системные ресурсы Номер регистра !ОН 12Н 14Н ISH 16Н 17Н l8H зон .31Н Высокий уровень Использование • тип накопителя НГМД тип накопителя фикc111xJ1i.1111101'tJ д11ска периферия память на.сt1стемной r~лате (м;~адший б.'lйт) память на с••стемной п;~ате (старший байт) общая память (младший m1йт) . о61щ1я щ...... ,ь (сщрший ба~iт) память сверх 1 мегабайта (младший бай1) ~амять сверх I мегабайта (старший байт) 17 J~ данной книге имеется множество примеров доступа к этим портам.. Ниже приводится программа на Бейсике, устанавливающая число· дисковых накопителей, при.соединенных к IВМ РС. Прежде чем прочитать два старших бита порта А, бит 7 порта В дnлжен бЬJть установлен в 1. Вы должны вернуть значение этоrо б~та назад в О перед дальнейшей работой, иначе клавиатура будет заперта и для восстановления работоспособности машины вам придется выключить ее.· Бейсик не позволяет двоичное . представление· чисел: что затрудняет работу с цепочками битов. Простая подпрограмма может заменить любое целое число вплоть до 255 (максимальное значение, которое может принимать номер порта) на восьмисимвольную двоичную , строку.. После этоrо строковая функция MID$ позволяет. вырезать нужные биты для анализа. Основы битовых операций в Бейсике описаны в приложени~ .& . 1!)() N. = INP(&H61 > 110А=АOR128 120 OUT &Н61,А 130 В = INP(&H60) 140А=АAND128 150 Щ-!Т &Н61,А 160 GOSUB 1000 170 NUMDISI<.$ = RIGHn(8$,I) 180 IF D$ =. 1 ТНЕN NUMDISK 190 С$ = LEFТ$ЦJ$,2) ·110J1учаем з11а•1е11ие ••з порта В ·уста11:11mш~.'lем б~п 7 ·посы;1аем байт 11азад 11 порт В ·r10;1y•raeм з11nче1ше из порта А ·сбр..'lсы1~аем бит., 7 ·щiсста1ш11.11иu.'lе~ зна•1ение порта В ·преобр,азуем в доои•iную строку ·получаем нулеоой бит = О: GOTO 230 ·нет дttсков 'бере,м два стЬрuшх бита строки . l •
18 Глава 200 TALLEY = О переменная дли •шсла дисков 210 IF RIGHT$(C$,I) "1" THEN TALLEY = 2 'берем старншй бит 220 IF LEFГ$(C$,I) = "1" THEN TALLEY = TAIJ,EY + 1 ·и младший 230 TALLEY = TALLEY + 1 ·с11ет 11ачи1шстсi1 с 1, а 11с с О 'теперь ~,меем число накопителей 1000 '"Подпрограмма 11реобразо11аш1и байта 11 д1юич11ую строку 1010 8$ = .... 1020l'ORN=7ТООSПоР-1 1030Z=8 -2·N 1040lFZ>= ОTHEN8=Z:8$ 1050 NEXT 1060 RETURN Низкий уровень ЗШIO,',lflM строку ·11ро11срка 011срсд1юй стснсш1 2 В$+"1"ELSEВ$=В$+"О" •повторяем для каждого бита все З.1KOH'ICIIO Ассемблерная программа получает число имеющихся дисковых накопителей тем же способом, что и в приведенном выше примере, но более просто. Напоминаем, что' НСЛl,ЗЯ забыватt, о восстановлении первоначального значения в порте В. lN АL,61Н ;11олучасм з1~ачс11и~ нз 11orпaJl OR AL, 100000008 ;уста11аw1ющсм бfп 7 11 1 OUT 61Н,АL ;заменяем б.1йт IN AL,60H ;получаем зна11енне из порта А МОУ CL,6 ;подготовка для сдш1гd AL SНR AL,CL ;сдвигаем 2 старших бита на 6 позиций INC AL ;начинаемсчетс1,анесО МОУ NUM_DRIVES,AL ;получаем •шсло накопителей IN АL,61Н ;подготовка к 11Ьсста11овлеш1ю ,юрта В AND AL,0 1111111 В ;сбрасываем бr.т 7 OUT 61Н,АL ;восстанаuли1.щем байт 1.1 .1 . Определение типа IBM РС Существуют проблемы совместимости между различными типами IВМ РС. Для того чтобы программа могла работать на любом из IBM РС, используя все его возможности, необходимо, чтобы она могла определить тип машины, в которую она загружена. Эта информация содержится во втором с конца байте памяти по адресу .FFFFE в ROM-BIOS. Ключевые числа следующие:
Систем11ые ресурсы Ком11ыотер РС хт J>Cjr АТ 8ысокий уровень 19 В Бейсике надо просто использовать РЕЕК для чтения значения: 100 DEF SEG = &1·11. -00 0 110 Х = J>EEK(&HFFFE) 120IFХ =&HFDTHEN ... Низкий ур9вень· В языке ассемблера: ;--- определение типа компьютера: ·указьшаем на 11ерхние 64К памяти ·читаем второй с ко111щ байт •... то1-д:1 это PCjr MOV: AX,0FOO0H ;указывает ES на ГIЗУ MOV ES,AX MOV AL,ES:[0FFFE!-1) ;rюлучасм байт CMJ> ЛL,0FDH ;это PCjr. '! . JE INITIAJ,JZE _JR ;нсреходим 1111 ~111ш1иаJшзащщJ 1.1 .3 . Опредеnение версии MS-DOS По мере развития MS-DOS ее возможности расшир~ись. Это существенно облегчило написание определенных частей программы по сравнению с предыдущими версиями. Чтобы иметь гарантию, что программа будет работать с любой, версией MS-DOS, она должна использовать только функции, доступные в MS-DOS 1.0. В системе предусмотрено прерывание, возвращающее номер версии MS-POS. Это число может служить для проверки выполнимости вашей программы. При старте программа может выдавать сообщение, что ей нужна другая версия MS-DOS. Средний уровень . Функция ЗОН прерывания 21 Н возвращает· номер версии MS-DOS. Старший номер версии <2 из 2.10) возвр_ащается в AL, а
20 Глава 1 младший номер версии <1О из 2.10) возвращается в АН (обратите внимание, что младщий номер .1 возвращает значение АН, а не lH). AL может содержать О, что указывает на версию MS-DOS, более раннюю, чем 2.0. Это прерывание меняет содержимое регистров ВХ и СХ, в которых возвращается значение О. ;--- определение версии MS-DOS: MOV АН.ЗОН ;ном.ер функции 1ю:~уче111н1 нсрс1111 INT 21Н СМР Al,,2 JL WRONG_DOS ;получить номер 11срс1111 ;проверка на версию 2.х ;ес.111 меш,ше 2, то 111,11щт~, сообщс1111с 1.1 .4 . Оnредеnенме чнсnа н тнпов адаптеров днсплея Программе может оказаться необходима информация о том, будет ли она работать в системе с монохромным адаптером, с цветной графической картой или с EGA, а также информация о наличии второго адаптера. В параграфе [4.1 .6] объяснено, как передать упра11ление от одного адаптера к другому. Байт статуса оборудования, хранящийся в области данных BIOS по адресу • 0040:0010, сообщает установку пере·к,1ючателя 1, который .показывает, какая из карт активна. В принципе биты 5-4 должны иметь значение 11 для монохромной карты, 1О - для цветной карты 80*25, 01 - для цветной карты 40*25 и 00 - для EGA. Однако при наличии EGA он может установит,, значение битов отличным от 00, в зависимости от установки его собственных переключателей. Поэтому вы должны сначала с помощью других средств определит~, наличие EGA, а затем, если его нет, по данным BIOS определить, является ли активным цветной или монохромный адаптер. Для проверки наличия EGA надо прочитат,, байт по адресу 0040:0087. Если он равен О, то' EGA отсутствует. Если этот байт нс нvлевой, то, когда бит З = О, EGA является активным адаптером: а когда он равен 1. то активен второi1 адаптер. Когда присутствует EGA, проверка наличия монохромного или цветного адаптера осуществляется зап11с1,ю значсн11я в регистр адреса курсора микросхемы 6845 14.1. l 1 11 последующего чтения н проверки значений на совпадение. Для монохромной карты пошлите 0FH в nорт 3В4Н, чтобы указап, на регистр курсора, а затем прочитайте и запишите адрес курсора через порт 3В5Н. Соответствующие порты для цветной карты - 3D4H и 3D5H. Когда карта отсутствует, порт возвращает значение 0FFH; но поскольку это значение может содержаться в регистре. простой проверки на него недостаточно.
Системные ресурсы 21 При наличии EGA могут потребоваться ответы на следующие два вопроса: сколько имеется памяти на его карте и какой тип монитора подсоединен? Для определения типа дисплея проверьте бит 1 по адресу 0040:0087; когда он установлен, подсоединен монохромный дисплей, а когда он равен нулю - цветной. Если ваша программа использует цветной графический режим с 350 строками, то надо также выяснить, какой дисплей присоединен; IRGB или R'G'B'RGB, где последняя аббревиатура соответствует улучшенному цветному дисплею IВМ. Это определяется установкой четырех vпереключателей на карте EGA. Установка этих переключателеи возвращается в CL при обращении к функции 12Н прерывания l0H. Цепочка четырех младших битов должна быть 0110 для улучшенного цветного дисплея. Та же функция сообщает и о наличии памяти на карте EGA. Она .возвращает BL, содержащий О для 64К, l для 128К, 2 для 192К и 3 для полных 256К памяти дисплея. Высокий уровень Приведенные фрагменты кода определяют тип текущего монитора и режим его работы, а также типы видеоадаптеров в машине: 100 ···определение активно~~ адаптера 110 DEF SEG = &Н40 120 Х = РЕЕК(&Н87) 130IFХ=ОTHEN200 140IFХAND8=ОTHEN... 200 Х = РЕЕК(&НlО) 21ОУ=ХAND48 220IFУ'= 48THEN... 230IFУ 32THEN... 240IFУ = 16THEN... ·указыuаем 1щ об;шсп, данных BIOS ·проверка 1ш налич11е EGA "EGA отсутствует, идем дальш~ ·акти1111ый монитор EGA ·ч~паем ба.°п статуса оборудования ·выделяем б1пы 4 11 5 •. . . т о 1· д а мо,юхромный <00110000) •. . . т огд а цнетной 80*25 (00100000) ... тогда цнетной 40*25 (00010000) Следующий пример иллюстрирует проверку наличия монохромной карты, когда активнои является карта EGA или цветная. Он может •служить и для проверки наличия цветной карты, если использовать адреса портов &H3D4 и &HЗDS.
22 Глава 1 100 ···проuерка наличия монохром1юii карты 110 OUT &НЗВ4,&нr: 120 Х = INP(&HЗBS) 130 OUT &НЗВS,100 140 IF INP(&HЗB5)<>100 THEN ... 150 OUT &НЗВ5,Х Низкий уровень анрсс рсI·нстра курсора ·чтсш1е и сохра~Iеш1е :111ачс11ия ·1юсылаем II реп1стр любое з1шчс11не ·если карта есп, - вернется то же восстанаuлиuасм зна•1ен~1е регистра Предлагаемые ниже примеры соответствуют примерам на Бейсик~ приведенным ранее: ;--- определение активного адаптера: MOV АХ,40Н ;указьшаем l:S 1ш облает~ щ11шых В/ОS моv ES,AX MOV AL,ES: [87Н] ;nроuеряем 11алн11~1е EGA СМР AL,0 JE NO EGA TEST AL,000010008 ;если 0040:0087 = О, то ЕGЛ нет ;EGA ест~,, 11ровсряем бит З JNZ EGA NA ;если б~п З = 1, то ЕGЛ неактивен EGA NЛ: моv - Al,,ES: l 10111 ;IIровер>1см баi-iт статуса ,1 1ис11лс~1 AND AL,oo 1 1oooon ;выдс;1s.1см б~п 1)1 4 и 5 СМ!' ЛL,48 ;:но мо11(,хром11а~1 ка~)"1а? JE MONOCHROME ;11срсхо,1 ,,ссли ;нt Предполагая наличие монохромной карты, установлена ли цветная карта (неактивная): ;--- устаноuлена ли 11еакти111шя цuепшя карта? MOV DX,3D4H ;указьшаем на реI·нс-Iр а:1рсса 6Х45 MOV AL,0FH OUT DX,AL INC DX IN AL,DX XCNGAH,AL MOV AL,100 OUT DX,AL ;запраш~шасм ре1·~~стр куrкора ;указываем Iщ рсI·~1сI р ;указываем t1a рспн.:тр :1а11111)1х ;r1олучаем тскунLСС з11а 1 1с11ис ;сохраi1~см з1~ачс11ис ;rесто1юс зIшчсш1с 100 ;посылаем еI·0 проверим,
Системные ресурсы IN AL,DX СМР AL,100 JNE NO_CARD XCNGAН,AL OUT DX,AL ;считываем его снова ;сравниваем значения ;переход, если нет карты ;иначе, есть цветная карта ;тогда восста11авливаем значение 1.1,S. Определение числа и типа дисковых накопителен 23 На всех машинах, кроме АТ (который будет обсуждаться ниже) , регистры микросхемы 8255 интерфейса с перифt:;рией содержат информацию о том, сколько НГМД имеет машина. В примерах из [1.1 :1 ] показано, как получить эту информацию. Информация, определяющая тип диска, находится в таблице размещения файлов (FАТ) диска, которая следит за использованием дискового пространства. Первым байтом в FАТ может быть один из следующих кодов: fuш FF FE FD FC F-9 F8 Тип диска двухсторонний, 8 секторов односторонний, 8 секторов двухсторонний, 9 секторов односторо1111ий, 9 секторов двухсторонний, 15 секторов фиксирова~шый диск Таблица размещения файлов не является файлом. Она может быть считана при помощи функций DOS или BIOS, непосредственно читающих определенные секторы диска. В параграфе [5.1 .1] содержится вся информация, необходимая для нахождения и чтения FАт.· К счастью, операционная система обеспечивает функцию, которая возвращает идентификационный байт диска. ',. • , Данные BIOS не показывают число жестких дисков в системе, так как переключатели предназначены только для гибких дисков.· Однако вы можете использовать указанную функцию операционной системы для поиска накопителей. Она возвращает значение 0CDH вместо одного из упомянутых кодов, когда нgкопители отсутствуют. Надо просто проверять ~се большие и большие номера накопителей до тех пор, пока не будет обнаружено указанное значение.
24 Г.лава 1 .АТ уникален в том смысле, что его информация о конфигурации говорит, какой тип накопитслs~ используется. Эту информацию можно получить из порта с .адресом 71Н, предварительно послав номер регистра в порт 70Н. Для НГМД номер регистра равен lOH. Информация о первом накопителе содержится в битах 7-4, а о втором - в битах 3-: -0 . В обоих случаях цепочка битов 0000 говорит об отсутствии накопителя, ООО 1 - о дnухстороннем накопителе с плотностью 48 дорожек на дюйм, а 001 О - о -накопителе большой емкости (96 дорожек на дюйм). Информация о фиксированном диске содержится_ в регистре 12Н. И снова биты 7-4 и 3-0 соответствуют первому и второму накопителям. 0000 указывает на отсутствие накопителя. Другие 15 возможных значений описывают емкость и конструкцию накопителя. Эти коды сложные; если вам по какой-то причине потребуется эта информация, обратитесь к техническому руководству по АТ. Средний уровень Функция lCH прерывания 21Н возвращает информацию об ука~анном накопителе. Поместите номер накопителя в DL, причем О = накопитель по умолчанию, 1 = А и т.д. При возвращении DX содержит число кластеров в FAT, AL - число секторов в кластере, а СХ - число байтов в секторе. DS:BX указывает на байт, содержащий код идентификации диска из FАТ, согласно приведенной таблице. В следующем примере определяется тип накопителя А: ;---оnре~еление типа диск? MOV AH,ICH ;функция MS-DOS MOV DL,l ;ныбор накоnитеJ1я А INT 21 Н. ;nолучен~1е информации MOV DL, [ВХ] ;nолучен~1е типа 1шкошпеля СМР DL,0FDH ;двухсторон~1ий, 9 секторов? JE DBL_9 ;и т.д. . BIOS АТ имеет функцию, сообщающую общие параr.tетры • накопителей. Это функция 8 uрерывания lЗН. Она возвращает число накопителей & DL, максимальное число сторон накопителя - в DH, максимальное число секторов - в CL и дорожек - в СН, а код статуса ошибки накопителя - в АН (см. параграф [5.4.8 ]). Другая функция BIOS АТ. возвращает тип накопителя. Это функция 15Н прерывания IЗН, которая требует номера накопителя в DL, В АН возвращается код, причем О нет
Системные ресурсы 25 накопителя, = дискета без обнарvжения изменений, 2 диск~та 'с обнаружением изменений и 3 • = фиксированный диск. В случае фиксированного диска в CX:DX возвращается число секторов по 512 байт. 1.1 .6 . Определение чисяа и типа периферийных устройств При старте ROM-BIOS проверяет присоединенное оборудование, сообщая о результатах своей проверки в регистр сmтуса. Этот регистр занимает .два байта, начиная с 0040:0010. Приведенные ниже значения битов относятся ко всем машинам, пока не оговорено обратное: бит о 2-3 4-5 6-7 8 9-11 12 }3 14-15 если 1, то присутствует НГМД ХТ,АТ:1 = есть математический сопроцессор (PC,PCjr:нe используется) 11 = базовая память 64К (АТ:11е испол1,зуется) Активный видеоадаптер ( 11 = мо11охром11ыt1, 10 = цветноf1 80*25, 01 = цветной 40*25) числ~ НГМД (если бит О = 1> PCjr:0 = есть DMA (РС,ХТ,АТ:11е используется) число адаптеров коммуника~щи 1 = есть игровой порт (АТ:11е испол1,зуется) РСjr:есть серийный принтер (РС,ХТ,АТ:не ис1юл1,зуется) число присоединенных 11рюперо11 Большая часть информации расшифровывается примитивно. Но обратите внимание, что инф9рмация о дисковых накопителях распределена между битами О и 6-7. :3начение О в битах 6-7 указывает, что имеется один дисковый накопитель; чтобы узнать об отсутствии накопителей, надо проверить бит О. . • Число· портов коммуникации может быть получено из области данных BIOS. BIOS отводит четыре 2-байтовых поля для хранения базовых адресов вплоть до четырех СОМ портов (MS-DOS использует только два из них). Базовый адрес - это младший •из адресов портов, относящихся к группе портов, имеющих ·доступ к данному каналу коммуникации. Эти четыре поля начинаются с адреса 0040:0008. Порту СОМI соответствует адрес :0008, а СОМ2 - ОООА. Если это поле содержит О, то соответствующий порт отсутствует. Таким образом, если слово по, адресу :0008 отлично от нуля, а по адресу ОООА - нулевое, то имеется один порт коммуникации.
26 Глава 1 АТ хранит информацию о периферии в регистре 14Н микросхемы конфигурации. Сначала запишите 14Н в порт с адресом 70Н, а затем прочитайте содержимое регистра через порт 71Н. Вот значение битов эtого регистра: биты 7-6 5-4 3-2 1 о Высокий уровень 00 01 1НГМД,01 =2НГМД вывод на цветной дисплей, 40 строк 1О вывод на цветной дисплей, 80 строк 11 вывод на. монохромный дисплей не используется имеется математический сопроцессор О = нет НГМД, 1 = имеется НГМД В Бейсике нужно просто прочитать байты статуса из области данных BIOS. В приложении Б объяснено выполнение битовых операций в Бейсике. В приведенном примере проверка наличия дисковых накопителей достигается проверкой четности младшего байта статусного регистра (четный - нет накопителей). 100DEFSEG=О 110 Х = РЕЕК(&Н410) 120IFХMOD2=ОTHEN140 130 PRINT "Имеется диск" 140 GOTO 160 150 PRINT "Нет накопителей" 160 ... Проверка наличия COMl: ·указываем на дно памяти ·получаем младший байт регис:rра ! ·он четный - нет накопителей и11аче - имеется накопитель " ·идем ко второму сообще11ию •второе сообщение ·продолжаем ... 100 DEF SEG = 40Н ·указываем на область данных ВIOS. l.10 PORT = РЕЕК(О) '+ 256*РЕЕК(l)'получаем слово со смещением О 120 IF PORT = О THEN... •... тонет адаптера СОМ! Средний уровень Прерывание 11 Н BIOS возвращает байт статуса оборудования в АХ. На входе ничего подавать не надо. В примере определяется число дисковых накопителей.
·сuстемиые ресурсы ;---получение числа дисковых накошпслсй: INT IIН TEST AL,0 JZ NO DRIVES AND AL, 11 ОООООВ MOV CL,5 SHR AL,CL INC AL Низкий уровень ;получаем байт статуса ;име1отс~ 11ако111-1тt::л~1'? ;IIсрехо:1. есш1 11е·I ;ш,щел11см биты 5 ;1юд1'0то11ка к сд11и1-у реI·истра ;сдвиг 1тра110 1ш 5 бfп ;добамяем 1, так как отсчет идет с 1 27 Ассемблерная программа работает так же, как и программа на Бейсике. Пример иллюстрирует чтение информации о конфигурации для АТ и определение, установлен ли математический сопроцессор: MOV AL,14H OUT 70H,AL IN АL,71Н TEST AL,108 JZ NO_COPROC ;номер ре1·истра ;1юсылаем занрос ;•штаем регистр ;нровер~Iем бfiт 1 ;если ~е установлен. то со~Iроцсссора нет 1.1 .7 . Ревизия количества памяти Вопрос: "Сколько имеется памяти'?" - может быть задан в трех смыслах. О каком количестве памяти сообщают переключатели, установленные на системной плате? Сколько микросхем памяти реально установлено в машине? И наконец, сколько остается· свободной памяти, которую DOS, . может использовать для выполнения ваших программ? Машина может иметь 10 банков памяти по 64К, но переключатели способны указывать на наличие только 320К, оставляя половину памяти для каких-либо специальных целей. А как ваша программа узнает, сколько из доступных 320К она может использовать, учитывая, что другое программное обеспечение может быть загружено резидентным в верхнюю или нижнюю часть памяти? _Ответ на каждый вопрос ~ожно получить определенным способом. Для РС и ХТ установка переключателей может быть просто прочитана через порт В микросхемы интерфейса с периферией 8255. В параграфе [ 1.1.1 ) описано, как это делается. BIOS хранит двухбайтовую переменную по адресу 0040:0013, которая сообщает число килобайтов использvемой памяти. Для PCjr бит З порта 62Н (порт С микросхемы· 8255) равен нулю,
28 Глава 1 когда машина имеет добавочные 64К памяти. АТ даст особо полную информацию о памяти. Регистры 15Н <младший) и lбН (старший) микросхемы информации о конфигурации сообщают, сколько памяти установлено на систсмноii плате (возможны три значения: О\ООН для 256К, 0200Н для 512К и 0280Н для 512К плюс 128К на плате расширения). Памят~, канала ввода/вывода • для АТ сообщается регистрами 17Н и 18Н <с инкрементом 512К). Память сверх 1 мегабайта доступна через регистры ЗОН и ЗIН (с инкрементом 512К вплот1, до 15 мегабайт). Ес,,и АТ имеет 128К на плате расширения, то установлен бит 7 регистра 33. Во всех случаях надо сначала послать номер регистра в порт 70Н, а затем прочитать значение из порта 71 Н. Легко написать программу, которая прямо тестирует наличие памяти через опреде~,енные интервалы адресного пространства. Поскольку минимальная порция памяти - 16 килобайт, достаточно проверить одну ячейку памяти в каждом 16-килобайтовом сегменте, чтобы убедиться, ч1;0 все IбК присутствуют. Когда данная ячейка памяти отсутствует, при чтении из нее получаем значение 233. Для проверки можно з~tписат~, в ячейку произвольное число, отличное от 233, и сразу же с,1итат1, его. Если вместо посланного числа возвращается 233, то соотвеп:твующий банк памяти отсутствует. Нс применяйте :JТот способ на АТ, где при попытке писать в несуществующую пам~1т1, вступает в действие встроенная обработка несу шествующей памяти. Диагностика АТ настол~,ко хороша, что вы можете целико,м положиться на системную информацию о конфигурации. Память постоянно. занимается частями операционной системы, драйверами устройств, ре.зидентн ы ми п porpa м мам и обработки прерываний, и управляющими блоками MS-DOS. При проверке банков памяти вы не должны вносить необратимых изменений в содержимое памяти. Сначала надо • сохранить значение, хранящееся в тестируемой ячейке, затем проверит~, ее и восстановить первоначальное значение. Имеется еще одна проблема. Если ваша процедура хотя бы временно модифицирует свой код, то :по может привести к сбою. Поэтому для проверки надо выбират1, такую ячейку из блока 64К, которая не будет занята текстом вашей процедуры. Для этого поместите процедуру тестирования впереди программы, а для тестирования выберите ячейку со смещением, равным смещению для кодового сегмента. Например, если регистр кодового сегмента содержит 13Е2, то сегмент начинается со смещения ЗЕ2 во втором 64-килобайтовом блоке памяти. Поскольку ваша подпрограмма проверки не может находиться по этому адресу, вы можете безопасно проверять ячейку ЗЕ2 в каждом блоке. Запрет прерываний (см. [1.2 .2 ]) позволяет нс беспокоиться о ~
Системные ресурсы 29 модификации кода из-за аппаратных прерываний, которые могут происходить во время проверки. Определение количества памяти, реально доступной операционной системе, также требует некоторого фокуса. Когда программа первый раз получает управление, DOS отводит ей всю доступную память, включая верхнюю область, содержащую нерезидентную часть DOS (которая автоматически перезагружается, если она была модифицирована). Для запуска другой программы из текущей или для ТOI'Q, чтобы сделать программу подходящей для многоnользовательской системы, необходимо урезать программу до требуемого размера. В параграфе [1.3 .1 ] описано, как это сделать с помощью функции 4АН лрерывания 21Н. Эта же функция может . быть использована для расширения от.веденной памяти. Поскольку программе отводится вся доступная память при загрузке, такое расширение невозможно при старте. Если вы попробуете сделать это, то будет установлен флаг переноса, в регистре АХ появится код. ошиб~и 8, а в регистре ВХ будет возвращено максимальное число доступных 16-байтовых параграфов. Эта информация как раз и нужна. Значит, надо вьщать запрос со слишком большим значением в регистре ВХ (скажем, F000H параграфов), а затем выполнить прерывание. Позаботьтесь о том, чтобы выполщ1ть эту функцию в самом начале программы, пока регистр ES еще имеет начальное значение. Высокий уровень Интерпретатор Бейсика :использует только 64К (хотя операторы РЕЕК и РОКЕ позволяют доступ к памяти за пределами 64К). Доля памяти, доступная в настоящий момент, возвращается функцией FRE. Эта функция имеет фиктивный аргумент, который может быть числовым или символьной строкой. BYTES = FRE (х) передает в BYTES число свободных байтов. BYTES = FRE(x$) делает то же самое. Но строковый аргумент вынуждает очистку области данных перед тем, как возвратить число байтов. Заметим, что если размер рабочей области устанавливается с помощью оператора CLEAR, то количество памяти, сообщаемое функцией FRE, будет на 2,5 - 4 килобайта меньше из-за потребностей рабочей области интерпретатора. Транслятор Бейсика не накладывает ограничение 64К на суммарный объем кода и данных. Но компилятор ограничен тем количеством памяти, которое он может использовать при компиляции. Если этого пространства недостаточно, то уничтожьте
30 Глtша все ненужные номера строк при помощи ключа компиляции / N. Можно также использовать более короткие имена переменных. Средний уровень Прерывание 12Н BIOS проверяет установку переключателей и возвращает в АХ количество килобайт памяти в системе. Эта величина вычисляется из установки регистров микросхемы 8255 или, для АТ, микросхемы конфигурации/часов. Входных регистров нет. Имейте в виду, что установка переключателей может быть неверной и это ограничивает достоверность такого подхода. Для определения числа 16-байтовых параграфов, допvпных для DOS, используйте функцию 4АН прерывания 21Н. ES должен иметь то же значение, что при старте задачи: ;---~пределение числа параграфоu,достуn111,1х д;1>1 DOS MOV АН,4АН ;указываем 11уж11ую Ф)'IIKI\HIO MOV BX,OFFFFH ;требуем слшнком бол1,111ую 11ам11п, INT 21Н ;ВХ содержит <1f1с;ю :юсту1111ых 11ара1·рафов АТ использует функцию 88Н прерывания 15Н для проверки наличия расширенной памяти. Эта функция ищет г:амять вне адресного пространства процессор<! в обычноrvt режиме адресации. Говорят, что она ищет память за отметкой I мегабайт. Чтобы эта функция работала, на системной плате должно быт1, от 512 до 640 килобайт памяти. Число килобайтовых блоков расширенной памяти возвращается в АХ. Низкий уровень В первом примере приведена проверка числа банков памяти по 64К в первых десяти 64-килобайтовых сегментах памяти. Если вы будете проверять старшие 6 банков памяти, то имейте в виду, что имеются видсобуфер, •начиная с В000:0000 (и, возможно, АООО:0000), и ПЗУ, .начиная с F000:0000 (и, возможно, СООО:0000). ;---11роuерка каждот ба11к;~ 11а"!11Тн: CLI MOV AX,CS AND AX,OFFI'H MOV 1':S,AX ;за11р~ 1· а1111арал1ых 11рсрыва1н-1й ;1юлучасм з1Iа 1 1с1I~1с ко;ю,_ЮI'<) Lс1·;\1с1па ;сбрасываем стар11н1с 4 Gн·1а ;помещаем укnзатсл1) в ES
Системные ресурсы моv DI,0 моv СХ,10 моv BL,'X' моv DL,ES:[0] моv ES:[0],BL моv DH,ES:[0] моv ES:[0],DL СМР DH,'X' JNE GO_AHEAD INC DI моv AX,ES ADD AX,IOOOH MOV ES,AX LOOP NEXT SТI ;DI считает число банков памяти ;будем проверять 10 банков ;для проверки используем -х· NEXT: ;сохраняем значение тестируемой ячейки ;помещаем -х· в эту ячейку ;читаем тестируемую ячейку ;восстанавливаем значение ;совпадает с тем, что nи.:али? ;если нет, то банк отсутствует ;увеличиваем число банков GO_AHEAD: ;готовим увеличение указателя ;указываем на следующие 64К ;возвращаем указатель в ES ;обрабатываем следующий банк ;разрешаем аппаратные nрерыв.'lния 1.2. Управление прерываннямн 31 Прерывания - это готовые процедуры, кото­ рые компьютер вызывает для выполнения определенной задачи. Существуют аппаратные и программные прерывания. Аппаратные прерывания инициируются аппаратурой, либо с системной платы, либо с карты расширения. Они могут быть вызваны сигналом микросхемы таймера, сигналом от принтера, нажатием клавиши на клавиатуре и множеством других причин. Аппаратные преры­ вания не координируются с работой программного обеспечения. Когда вызывается прерывание, процессор оставляет свою работу, выполняет прерывание, а затем возвращается на прежнее место. Для того чтобы иметь возможность вернуться точно в нужное место программы, адрес этого места (CS:IP) запоминается на стеке вместе с регистром флагов. Затем в CS:IP загружается адрес прог­ раммы обработки прерывания и ей передается управление. П рог­ раммы обработки прерываний иногда называют драйверами преры­ ваний. Они всегда заканчиваются инструкцией IRET (возврат из прерывания), которая. завершает процесс, начатый прерыванием, возвращая старые значения CS:IP и регистра флагов, давая тем самым программе возможность продолжить выполнение из того же состояния.
= " ~ : ; : : , . : : : , ~ § = ( ' " ; ; ~ ' О : : ~ ~ " ' : : : 1 з : : ~ с : , , 1 " ' ' - . н а n р а а n е н м е 1 к в е р х у 1 1 j n а м 1 1 т м Н а ч а л о 1 ! ! _ i N I Z i м j j j . 1 1 1 В о : s в р а щ а е м с 1 1 n o - - - - - - - - 1 1 1 , , , , • ' ' ~ ; ; _ , . . _ - ~ С о х р а н 1 1 е м ~ 1 c s I С т е к " " а д р е с ( C S : I P ) . . _ . . ; . : i : н а с т е к е - I N т - - · · _ L _ _ _ L _ _ _ _ _ _ _ _ . _ _ l И н с т р у к ц и и - ; 1 1 1 1 1 г И н с т р у к ц и и , C D I I I C D C D : о Н а ч и н а е м I I I I C D C D l : D П р о д о л ж а е м 1 1 : ! t i t o : m о : : о : l t l t : : : : J : : : : 1 : : : : 1 : : : : 1 - 4 : : : : 1 : : 1 : : : : 1 : : : : 1 о б р а б о т к у с g g g g 1 о т с ю д а g g g g э т о г о а д р е с а : с z z z Ж , х : : z : z : а : а : а : а i ; i ; i ; J ( D ( D ( D ( D : ! : : ! : : ! : : ! : : ! : : ! : : ! : : ! : ~ П о л у ч а е ~ C S : I D и з с т е к а 1 - П о n у ч а е м в е к т о р н о м е р 2 1 Н t J J - ~ ; ; о , t i : I ~
Системные ресурсы 33 С другой стороны, программные прерывания на самом деле ничего не прерывают. Это обычные процедуры, которые вызыва­ ются вашими программами для выполнения рутинной работы, такой, как обработка нажатия клавиши на клавж1туре или вывод на экран. Однако эти подпрограммы содержатся не внутри вашей программы, а в операционной системе, и механизм прерываний дает вам возможность обратиться к ним. Программные прерывания могут вызываться друг из друга. Например, все прерывания обра­ ботки ввода с клавиатуры DOS используют прерывания обработки ввода с клавиатуры BIOS для получения символа из буфера клавиатуры. Отметим, что аппаратное прерывание может получить управление при выполнении программного прерь1вания. При этом не возникает конфликтов, так• как каждая подпрограмма обработки прерывания сохраняет значения всех используемых ею регистров и затем восстанавливает их при выходе, тем самым не оставляя следов того, что она занимала процессор. Адреса программ прерываний называют .вектора.~,и. Каждый вектор имеет длину 4 байта. В первом слове хранится значение IP, а во втором - CS. Младшие 1024 байта памяти содержат векторы прерываний, таким образом, есть место для 256 векторов. Вместе взятые они называются таблицей векторов. Вектор для прерывания О начинается с ячейки 0000:0000, для прерывания 1 - с ячейки 0000:0004, 2 - с 0000:0008 и т.д. Если вы посмотрите н.1 4 байта, начиная с адреса 0000:0020, в .которых содержится вектор прерывания 8Н (прерывание времени суток). то вы обнаружите там A5FE00F0. Имея в виду, что младший байт слова расположен сначала и что порядок - IP:CS, это 4-байтовос значение переводится в F000:F'EA5. Это стартовый адрес программы ПЗУ, выполняющей прерывание 8Н. На рис. J.. 2 показана схема выполнения программой прерывания 21 Н. t.1 . t . Проrраммнрованне контроnnера прерь1ваннй 81S9 Для управления аппаратными прерываниями во всех типах IВМ РС используется микрос~ема программируемого контроллера прерываний Intel 8259. Поскольку в каждый момент времени может поступить не один запрос, микросхема имеет схему приоритетов. Имеется 8 уровней приоритетов. кроме АТ, • у которого их 16, и обращения к соответствующим уровням обозначаются сокращениями от IRQ0 до IRQ7 (от IRQ0 до [RQl5), что означает запрос на прерывание. Максимальный приоритет соответствует уровню О. Добавочhые 8 уровней для АТ обрабатываются второй микросхемой 8259; этот второй набор уровней имеет приоритет между IRQ2 и . IRQЗ. Запросы на 2 Р. Джордейн
34 Глава 1 прерывание 0-7 соответствуют нектарам прсрынан11й от 8Н до 0FH; для АТ запросы на прерывания 8-15 обслуживаются векторами от 70Н до 77Н. Ниже приведены назначения этих прерываний. IRQ О 1 - 2 8 9 10 11 12 13 14 15 3 4 5 6 7 А1111аратные лрерь111ания п лоря11кс 11рнорнтста таймер клавиатура ка11ал ввода/вывода •шсы рсалыю1'0 t1рсмеш1 (то:11,ко АТ) щюграммно 11срсво,1я·1с>1 в IR<)2 <н,т,ко ЛТ) резер11 рсзер11 резерв матемап1ческ~1~1 со11роцсссор (тот,ко АТ) контроллер фиксирова111ю1·0 диска (тот.ко АТ) резерв СОМ! (СОМ2 для А'Р СОМ2 (модем дли l'Cjr, COMI д;111 АТ) фиксированный диск (U'T2 для АТ) контроллер дискет LPTI Прерыванию времени суток 12.1 .0 1 дан максимал1,ный приоритет, поскольку се.ли оно будет постоянно терят1,ся, то будут неверными показания системных часов. Прерывание от клавиатуры (3.1.0] вызывается при нажати,и или отпvскании клавиши; оно вызывает цепь событий, котор~tя • обычно заканчивается тем, что код клавиши пdмещается в буфер клавиатуры (откуда он затем может быть получен программными прерываниями). Микросхема 8259 имеет три однобайтовых регистра, которые управляют восемью линиями ~шпаратных прерываний. Регистр запроса на прерывание (IRR} устанавливает соответствующий бит, когда линия прерывания сигнализирует о запросе. Затем микросхема автоматически проверяет, не обрабатывается .111 другое прерывание. При этом она запрашивает информащ11<.1 регистра обслуживания ([SR). Дополнител1,ная цсщ, отвечает за схему приоритетов. Наконец, перед вызовом прерыванш1 проверяется регистр маски прерываний <IMR), чтобы узна:гь, разрешено ли в данный момент прерывание данного уровю1. Как
Системные ресурсы 35 правило, программисты обращаются то,11,ко к рсrистру маски прерываний через порт 21 Н [ 1.2 .2 1 и командному рсrистру прерываний через порт 20Н [ 1.2 .3 \ . 1.1.1 . Запрет/ разрешенне от деnьных аппаратных прерываннй Программы на ассемблере моrут запретить аппаратные прерывания, перечисленные в [ 1.2.1 \. Это маскируемые прерывания; другие аппаратные прерывания, возникающие при некоторых ошибках (таких, как деление на ноль), нс моrут быть маскированы. Существуют две причины для запрета аппаратных прерываний. В первом случае вес прерывания блокируются, с тем чтобы критическая часть к;ода была выполнена целиком, прежде чем машина произведет какое-либо другое действие. Например,· прерывания запрещают при изменении вектора аппаратного прерывания, чтобы избежать выполнения прерывания, когда вектор изменен только наполовину. Во втором случае маскируются только определенные аппаратные прерывания. Это делается, коrда некоторые прерыва~ия могут взаимодействовать с опсрац11ями, критичными к временным интервалам. Например, точно рассчитанная по времени процедура ввода/ вы вода нс может себе позволить быть прерванной длительным дисковым прерыванием. Низкий уровень Выполнение прерываний зависит от значения флага прерывания (бит 9) в регистре флагов. Коrда :лот бит равен О, разрешены все прерывания, которые разрешает маска. Коrда он равен 1, все аппаратные прерывания запрещены. Чтобы запретить пре·рывания, установив этот флаг в 1, используется инструкция CLI. Для очистки этого флага и восстановления прерываний применяется инструкция, STI. Избегайте отключения прерываний на длительный период. Прерывание времени суток происходит 18,2 раза в секунду, и если к этому прерыванию был более чем один запрос в то время, когда аппаратные прерывания были запрещены, то лишние запросы будут отброшены и системное время будет определяться неправильно. Имейте в виду, что машина автоматически запрещает аппарат­ н_ые прерывания при вызове программных прерываний и автомати­ чески разрешает их при возврате. Когда вы пишете свои програм­ мные прерывания, начинайте программу с инструкции STI, сели можно допустить аппаратные прерывания. Отметим также, что 2*
36 Глава 1 если за инструкцией CLI нс следует SТI, то это приведет к оста­ новке машины, так как ввод с клавиатуры будет заморожен. Для маскирования определенных ,tпnаратн1,1х прерываний нужно просто послать требуемую цепочку битов в порт с адресом 21Н, который соответствует регистру м.~ски прерываний (IMR). Регистр маски на второй микросхеме 8259 для АТ ORQS-15) имеет адрес порта AlH. Уст,tновите те бит1,1 реrистр,t, которые соответствуют номерам прерываний, выбранным в,tми для маскирования. Этот регистр можно· только записывать. Приведенный ниже пример блокирует дисковое прерывание. Не забудьте очистить регистр в конце программы, иначе обращение к дискам будет запрещено и оосле завершения программы. ;---маскирование 6-1 '0 бита ре1"1стра маски 11рер1,11~111и~i • MOV ЛL.ОНКЮОООВ ;мс1ск~1русм бит 6 OUT 21Н,ЛL MOV ЛL.О OUT 21Н,ЛL ;1юсыJ111см 11 ре1·нстр маски 11pcp1,11iaш1r1 ;о•шщаем IMR 11 ко1щс 11р1)1'Рамм1,1 1.1 .3. Написание собственноrо прерьtвания Существует несколько причин для написания собственного прерывания. Во-первых, большинство из готовых прерываний, обеспечиваемых операционной системой, нс что иное, как обычные цроцедуры, доступные для всех программ, и вы можете пожел,tть добавить свое в эту библиотеку. Например, многие ваши программы могут испмьзовата, процедуру, выводящую строки на экран вертикально. Вместо того чтобы включат~, се в каждую программу в качестве процедуры,_ вы можете уст,шовить ее как прерывание, написав программу, которая останется резидентной в. памяти после завершения (1.3.4 ]: Тогда вы можете использовать INT 80Н вместо WRITE_VERTICALLY <имейте в виду, что вызов прерывания несколько медленней, чем вызов процедуры). Во-вторых, причиной написания прерывания может быть использование какоrо-либо отдельного аппар.tтноrо п·рерывания. Это прерывание автоматически вызывается при ·возникновении •определенных условий. В некоторых случаях BIOS инициали­ зирует вектор этого прерывания так, что он указывает на проце­ дуру, которая вообще ничего не делает (она содержит один опера­ . тор IRET). Вы можете написать свою процедуру и изменить век- тор прерываний. чтобы он указывал на нес. Тогда при возР1икно-
Системные ресурсы 37 вении аппаратного прерывания будет выполняться- ваша проце­ дура. Одна из таких процедур-это прерывание времени суток [2.1 .0 ], которое автоматически вызывается 18,2 раза в секунду. Обычно это прерывание только обновляет показание часов, но вы можете добавить к нему любой код. Если ваш код проверяет пока­ зания часов и вступает в игру в определенные моменты времени, то возможны операции в реальном времени. Другие возможности - это написание процедур обработки «Ctrl-Break» (3.2 .8 }, «PrtSc» (3.2. 9 ) и возникновения ошибочных ситуаций (7. 2.5 }. Прерывания принтера (6.3.1 ) и коммуникационные (7 .1 .8} позволяют компью­ теру быстро переключаться между операциями ввода/ вывода и друrой обрабО'J'кой. Наконец, вы можете захотеть написать прерывание, которое полностью заменит одну из процедур операционной системы, приспособленное к вашим программным ,нуждам. В [ 1.2.4} показано, ка·к написать прерывание внутри прерывания, позволяющее модифицировать существующие процедуры. Средний уровень Функция 25Н прерывания 21Н устанавливает вектор преры­ вания на указанный адрес. Адреса имеют размер в два слова. Старшее слово содержит значение сегмента (CS>, младшее содер­ жит смещение (IP). Чтобы установить вектор, указывающий на одну из ваших процедур, нужно поместить сегмент процедуры в DS, а смещение в DX (следуя приведенному ниже примеру). Затем • поместите номер прерывания в AL и вызовите функцию. Любая процедура прерывания должна завершаться не обычной инструк­ цией RET, а IRET. (IRET выталкивает из стека три слова, вклю­ чая регистр флаrов, в то время как RpT помещает на стек только два. Если вы попытаетесь тестировать такую процедуру как обыч­ ную процедуру, но коняающуюся IRET, то вы исчерпаете стек.) Отметим, что функция 25Н автоматически запрещает аппаратные прерывания в процессе изменения вектора, ~оэтому не существует опщ:ности, что посреди дороги произойдет аппаратное прерывание, использующее данный вектор. ;---установка прерыван~,я PUSHDS ;сохра11ясм DS MOV DX,OFFSET ROUT ;смсщс11нс ДJ1я пр<щсдуры u DX MOV AX,SEG ROUT ;сегме11т процедуры MOV DS,AX ;помещаем 11 DS MOV АН,2SН ;функция усташщк~1 IIскто1щ
38 MOV AL,60H INT 21Н РОР DS ;---процедура r1рер~.шан~1я ROUТINE PROC F.I\.R PUSHAX РОР АХ MOV AL,20H OUT 20H,AL IRET ROUТINE ENDP ;номер век1·ора ;меняем прер1,1щ111ис ;восста111111щшасм DS Глава l ;сохр.1ш1см IICC ИЗМСШIСМЫС рс1·~1стр1,1 ;восста11а11J1~1щ1см рсгистр1,1 ;эти дос строки надо 11c11om,зo1iaтt, ;только д.11я ап11арат111,1х прерываний В .конце кода каждого из ваших аппаратных прерываний вы должны включить следующие 2 строчки кода: MOV ЛL,20Н OUT 201:-1,AL, Это просто совпадение, что числа <20Н> одни и те же в обеих строках. Если аппаратное прерывание нс заканчивается этими строками, то микросх.ема 8259 не очистит информацию регистра обслуживания, с тем чтобы была разрешена обработка прерываний с более низкими уровнями, чем только что обработанное. Отсутствие этих строк легко может привести к сбою программы, так как прерывания от клавиатуры скорее всего будут замороженными и даже Ctrl-Alt-Dcl окажстсs~ бесполезным. Отметим, что эта добавка нс нужна для векторов прерываний, являющихся _расширениями существующих прср1,1ваний, таких, как прерывание lCH, которое добавляет код к прерыванию времени суток [2.1. 7 J. Когда программа завершается, должны быть восстановлены оригинальные векторы прерываний. В противном случае последующая программа может вызвать данное прерывание и передать управление на то место в памяти, в котором вашей процедуры уже нет. Функция 35 прср1>1вания 21 Н возвращает текущее значение вектора прерывания, помещая значение сегмента в ES, а смещение в ВХ. Перед установкой своего прерывания получите текущее значение вск·тора, используя эту функцию, сохраните эти значения и затем восстановите их с помощью функции 25Н (как выше) перед завершением своей· программы. Например:
Системиые ресурсы ~ - -- u сеrмс11те да1111ых: Кl:ШР_СS DW О KEEP_IP DW О ~ --- I S 11ачале IIро1-раммы MOV ЛН,251-1 MOV AL,ICH INT 21Н MOV KEEP_IP,BX MOV KEEP_CS ,ES ;---11 конце программы CLI PUSHDS MOV DX,KEEP 11' - MOV AX,KEEP_CS моv DS,AX MOV АН,25Н MOV ЛL,ICII INT 211-1 РОР DS STI ;храшп ссI·мсm замсш1с~юl'О 1Iрср1,111аш111 ;храшп СМСЩСШIС IIJ>CJJl,IЩl!IШI ;фу11кцш1 Iюлучс11ш1 векнJра ;номер 11сктора ;тс11срь CCl'MCIП 11 ES, СМСЩСШ1С II нх ;запоми1шсм CMCIL~Cl15-IC ;заrюмн1шсм сеl'мсIIт ;DS будет разрушен ;110дюто11ка к 1юсста1ю11лсш1ю ;1юд111товк1~ К IIOCCТШIOIIJICШIIO ;функ11ш1 устшювкн вектора ;1_юмср векн,ра ;11осста11а11ли11аем вектщJ ;восста11аI1.н~111асм OS 39 Сущоствуют "ловушки", которых следует избсrат1, при _написании прерывания. Если новая процедура прсμывания должна иметь достvn ·к данным,. то необходимо позаботип,ся, чтобы DS был пра~ильно установлен (обычно прерывание может использовать стек вызывающей программы). Другая неприsпнщ:тI, может заключаться в том, что при зансршснии программы по «Ctrl-Break» вектор_ прерывания нс будет восстановлен, если только вы не предусмотрите, чтобы программа рсакцю1 на <<Ctrl-Break>> выполняла эту процедуру 13.2 .8 1. Низкий уровень Описанные выше функции MS-DOS просто полvчают или изменяют пару слов в младших ячейках памяти.· Смещение вектора может быть вычислено простым умножением номера вектора на 4. Например, чтобы получип, адрес прерывания lбН в ES:BX, нужно: ;---получение адреса 11рср,,111ш1ш1 1611 SUR АХ.АХ ;уста11ш1ш111асм ES 1Iа Iш•1а:ю 1шм,п11
40 моv MOV SHL SHL моv MOV MOV ES,AX, DI,16H DI,I Dl,1 nx,ES:[DI] AX,ES: [DI] + 2 ES,AX ;номер прерывания в DI ;умножаем на 2 ;умнсжаем на 2 ;берем младший байт в ВХ ;берем старший байт в ES Глава 1 Не рекомендуется прямо устанавливать вектор прерываний, обходя функцию DOS. В частности, в многозадачной среде операционная система может поддерживать несколько таблиц векторо~ прерываний, и реальный физический адрес таблицы может быть известен только DOS. • t.1 .4 . Допоnненне к существующему прерь1ванню Хотя и не часто, но . бывает полезно добавить_ код. к существующему прерыванию. В качестве примера рассмотрим программы, которые пl)еобразуют . одно нажатие клавищи в длинные определяемые пользователем символьные строки (макроопределения клавиатуры). Эти программы используют тот факт, что весь ввод с клавиатуры поступает через функцию О прерывания '16Н BIOS 13.1.3 ]. Все прерывания ввода с клавиатуры DOS вызывают прерывание BIOS для получения символа из буфера клавиатуры. По::>Тому необходимо модифицировать лишь прерывание lбН таким образом, чтобы оно служило шлагбаумом для макроопределений, после чего любая программа будет получ,1ть макроопределения независимо от того, какое прерывание ввод.~ с клавиатуры она использует. Конечн.о, модифицировать прерывания BIOS и DOS непросто, поско:н,ку BIOS расположена в ПЗУ, а . DOS поступает без листинга и они ограничены размерами отведенной для них памяти. Но вы можете написать процедуру, которая прсд_шсстnуст и/ или с.1едует за соответствующим прерыванием, и :л.1 процедура может вызываться при вызове прерывания DOS или BIOS. Например, в случае прерывания 16Н вам. нужно написат1, процедуру и указать на нес вектором прерывания для \бН. Оригинальное значение вектора 16Н тем временем переносится в какой-либо неиспользуемый вектор, скажем бОН. Новая процедура просто вызывает прерывание бОН, чтоб1>1 испол1,зоват1, оригинальное прерывание 16Н; nо::>тому когда программа вызывает прерывание 16Н, управление передастся вашей процедуре; она затем вызывает
Системные ресурсы Таблица векторов ~q4- ~ S/0 ,1,~ ~tt) ~~ ~о INT90H ~~ !s• .. "'Q. a.cn 1 11., е,1 о 11) ::i;~I ~-.:; 1 ------ 1f1 с[о:1 f,~/ ~ 111 е+",, ,1о :'\ ~'1, .., .// ~"' . ~ ' .,,,,,._о~ е+' ~ INT16H .. "' ~ Q. 11., ~ J,, о t.,. 11 t'~ (t. о:,. t',i,.I, с[ о q, ;w ....:. RET • Код инициализации - INT 90H RET - INT16H Продолжаем Обработчик прерывани11 16Н Ваш обработчик прерывани11 Вызыьающа11 проrрамма Начинаем отсюда Рис. 1.3 . Доnолщ;ние к сущесп1ующему IIрерI,IшIшIю 41 оригинальное прерывание lбН, которое по :3авершснии снова возвращает управление вашей процедуре, а из . нее вы возвращаетесь в то место программы, откуда поступил вы~ов прерывания 16Н. После того как это сделано, в новой процедуре может содержаться любой код как до, так и после вызова
42 Глава 1 прерывания бОН. На рис. l.3 показана диаграмма ::>Той ,процедуры. Вот краткая 1::водка необхdдимых действий: 1. Создать новую процедуру, вызывающую прерывание бОН. 2. Перенести вектор прерывания для 16Н в бОН. 3. Изменить вектор lбН, чтобы он указывал на новую процедуру. 4. Завер~ить программу, оставляя се рt:зидснтноii 11.3.4 1- 1.3 . Управление программами Большинство программ заr;ружаются в память, запускаются, а затем удаляются операционной системой при завершении. Языки высокого уровня, обычно нс имеют альтернативы. Но для программистов на ассемблере существует другая возможность, и данный раздел демонстрирует се. Некоторые программы действуют как драйверы устройств или драйверы прерываний и они должны быть сохранены в памяти , ("резидентными") даже после их завершения (векторы прерываний обеспечивают механизм, посредством которого последующие программы могут обращап,ся к резидентным процедурам). Иногда программе необходимо запустит~, другую программу. DOS позволяет программе загрузит~, в память вторую копию COMMAND.COM, которая может бып, использована как средство интерфейса с пользователем или выполнения команд типа СОРУ или DIR. Программы могут быть в двух форматах: .ЕХЕ или .СОМ. Программы первого типа могут ·бып, больше 64К, но они требуют некоторой обработки перед тем, как DOS загрузит их в памяп,. С другой стороны, СОМ-программы существуют прямо в том фор­ мате, который нужен для загрузки в память. СОМ-пр0граммы осо­ бенно полезны для коротких утилит. В обоих случаях код, состав­ ляющий программу, предваряется в памяти префиксом програм­ много сегмента (PSP). Это область размером \ООН байт, которая содержит информацию, необходимую DOS для работы программы; PSP также обеспечивает место для файловых операций вво­ да/вывода (5.3.5). При загрузке ЕХЕ-файла и DS, и ES указы­ вают на PSP. Для СОМ-файлов CS также сначала указывает на PSP. Отметим, что MS-DOS 3.0 имеет функцию, которая возвра­ щает номер сегмента PSP. Это функция 62Н прерывания 2lH; ей ничего не надо подавать на входе, а в ВХ возвращается номер параграфа.
CucmeJ.t1tыe ресурсы 43 Одна из причин, по которой интересно по,,ожение PSP, состоит в том, что его первое' слово содержит номер прерывания DOS, которое будет приводить к завершению программы. Когда выполняется последний оператор RET программы, значения на вершине стека указывают счетчику команд (регистр /Р) на начало PSP, таким образом, код завершения выполняется как следующая инструкция программы. Дальнейшее обсуждение см. в параграфах [1.3.4 J и [1.3 .6 ]. Для спр<;1вки приводим значение полей PSP: Смещение 011 21-1 4Н 611 АН ЕН , 12Н .16Н 2СН 2EI-I 5СН • t,ПJ IIOII Размер Iюл11 DW DW DW DD DD DD DD 22 байта DW' 46 байт 16 байт 20 байт 128 бай·1 З11а•1еш1е 1юмер фу11к1t~iи l)OS завср111с;1иi1 IIрш·рамм1,I размер 1шм11н1 11 1шр:11·рафах резерв д;1ш111ый 111,Iзо11 функции :1исIш·1чера [)OS адрес зш1ерше11шI (11',CS) адрес ш,1хощ1 по Ctrl~П1·cak <11',CS) адрес 111,Iхода rю кр~1нl'1еской ошибке резер11 номер пара1·рафа строкI~~рс11ы резер11 обласп, 1шрамстров 1 (формат 1:СВJ облает~, ш1рамстров 2 (ф<!рма·1 l:CBJ обласп, [)ТЛ Iю умолча~Iию/1ю:1у•шст кома~I111Iую строку 11роI·раммы 1.3.1. Маннпуляцнн с памятью Когда MS-DOS загружает программу, она помещается в младшую область памяти, сразу же за COMMAND.COM и установленными драйверами устройств или другими утилитами, которые резидентны в памяти. В этот момент вся память за программой отведена этой программе. Если программе нужна память для с9зда1:fИя области данньrх, то она может приближенно вычислить, где в памяти кончается ее к-од, и затем поместить требуемую область данных в любое место за концом кода. Для определения адреса .конца программы поместите в конце программы псевдосеrмент типа
44 ZSEG SEGMENT , ', SEG ENDS Глава, 1 В ассемблере IВМ РС ZSEG будет последним ссrментом, так как сегменты располаrаются в алфавитном порядке. В случае с друrими ассемблерами нужно дсйствитсл1,но поместить эти строки в конце программы. В самой проrраммс достаточно поставить оператор MOV AX,ZSEG, и АХ будет указывать на первый свободный сеrмснт памяти за программой. Такой подход неприменим, если программа прсдполаrаст наличие памяти, которой на самом деле нет. Он нс будет также работать в мноrопользовател1,ской среде, коrда несколько проrрамм могут делить между собой одну и ту же область адресов. Для решения этой проблемы MS-DOS имеет возможность отслеживать 640К системной памяти и отводить по требованию программы блоки памяти любоrо размера. Блок памяти - это просто непрерывная область памяти, cro максимал1,ный разf"СР определяется размером доступной памяти, в частности он может быть больше одноrо сеrмснта (64К). Если затребован слишком большой блок, то DOS выдает сообщение об ошибке. Любая возможность перекрытия блоков исключена. Кроме тоrо, MS-DOS может освобождать, урезать или расширять существующие блоки. Хотя программа не обязана использовать эти средства, удобно и предусмотрительно делать это. Некоторые функции DOS требуют, чтобы были использованы средства упр.~вления памятI,ю DOS, например завершение резидентной проrраммы 11.3.4 ·1 или вызов друrой программы из данной ( 1.3.2 1 . ,• Прежде чем отвести память, существующий блок (вся память от начала программы до конца) должен быть обрезан до размера проrраммы. Затем, при создании блока, DOS создаст 16-байтовый управляющий блок памяти, который расположен непосредственно перед блоком памяти. Первые 5 байт жоrо блока имеют следующее значение: бай-\- О ASCII 90, если 1юсJ1сдш1й блок 11 цсш>•1кс, ина'lе - ЛSCII 77 1-2 О, если блок ос11обождс11 3-4 размер блока 11 l 6-баrпо111,1х 11араrрафах DOS обращается к блокам по цепочке. Адрес первоrо блока хранится во внутренней переменной. Значение этой переменной позволяет DOS определить положение первоrо отвсденноrо блока, а из информации, содержащейся в нем, может быть найден
• Системные ресурсы 45 следующий блок и т.д., как показано на рис. 1.4. Как только вы начали использовать систему распределения памяти DOS, вы обязаны придерживаться ее. Если программа изменит содержимое управляющего блока, цепочка будет разорвана и DOS начнет выдавать сообщения об ошибке. ПOCJl8A11Mii Управn111ОЩИii Ynpaвn11t0щмii управn111ОЩ11ii начаnо бnок бnок бnок naМIIТ_:.F---- --- _ _ __ _ _ _ f ____ --Fл Амtес. переменной MS-005 ААрес Рис. 1.4 . Структура у11рар_r1яющеrо блока nамяп1 Конец ПIMIIТII .. MS- DOS обеспечивает три функции распределения памяти, номера от 48Н до 4АН прерывания 21Н. Функция 48Н отводит блок памяти,· а 49Н освобождает блок памяти. Третья функция <SETBLOCK) меняет размер памяти, отведенной для программы; эта функция должна быть использована перед двумя остальными. После ее выполнения можно спокрйно отводить и .освобождать блоки памяти. Программа должна· освободить все отведенные ею блGки, перед завершением. Иначе эта память станет недоступной при последующей работе. Средний уровень Все три функции распреде.цения памяти прерывания 21Н используют 16-битовый адрес начала блока памяти, с которым они оперируют. Этот адрес соответствует сегменту, с которого начинается блок (блок всегда начинается со смещения О данного сегмента). Таким образом, реальный адрес ячейки начала блока равен этому адресу, умноженному на 16. Для всех трех функций ВХ содержит число 16-байтовых разделов памяти (параграфов), которые будут отводиться или освобождаться. Если функция не может быть выполнена, то устанавливается фщ~r переноса, а в АХ возвращается код ошибки, объясняющий причину. Возможны три кода· ошибки: •
46 7 8 9 ра~рушен уnрамяющий блок nам11п1 недостаточно памяти для выnолне1шя фу11кци~1 неверный адрес блока памяти Глава 1 Функция отведения блока использует коды 7 и 8, а освобождения - 7 и 9, в то время как функция изменения блока использует все три кода. В следующем примере сначала отводится блок размером 1024 байта. При этом ВХ содержит требуемое число 16-байтовых параграфов, а при завершении стартовый адрес блока равен АХ:О (т.е. смещение О в сегменте со значением, содержащимся в АХ). Вторая часть примера освобождает этот же блок, как и требуется при завершении прогр.~ммы. В данном случае значение, полученное в АХ, помещается в ES. DOS следит за размером блока и знает, какое количество параграфов надо освободить. ;---отведение блока размером 1024 байта MOV АН,48Н MOV ВХ,64 ;1юмер функцш1 ;требуем 64 нара~·рафа INT 21Н ;11ытаемс>1 отвести блок JC ERROR ;обрабать111аем щш,бку 11 CJ1y•iae 11еудачн MOV BLOCK_SEG,AX ;иначе, сохраняем адрес блока ;---освобождаем тот же блок MOV AX,BLOCK_SEG МОУ ES,AX MOV АН,49Н INT 21Н ;получаем стартовый адрес блока ;помещаем е1'0 в ES ;11омер требуемой фу11к1tш1 ;освобождаем блок 11амитн Наконец, приведем пример использования функции 4АН. ES содержит значение сегмента PSP, т.е. самого первого байта памяти, с которого загружена программа. Это значение . присваивается ES при старте задачи. Для испол1,зования SETBLOCK надо либо вызывать :JТу функцию· в самом начале программы (прежде чем ES будет изменен), либо сохранить его начальное значение. ВХ содержит требуемый размер блока в 16-байтовых параграфах. Для определения этого размера поместите добавочный "исскуственный" сегмент в конец программы. В макроассемблере IВМ РС сегменты располагаются в алфавитном порядке, поэтому вы можете поместить его в любое место программы при условии, что его имя - это что-то вроде "ZSEG". (В других ассемблерах действительно помещайте фиктJtвный сегмент в конец программы.)
Системные ресурсы 47 Программа может прочитать позицию этого сегмента и, сравнивая ее со стартовым сегментом, получить количество памяти, требуемое самой программе. В момент загрузки программы и ES, и DS содержат номер параграфа самого начала программы в префиксе ·программного сегмента; для СQМ-файлов CS также указывает на эту позицию, но для ЕХЕ-файлов :,то не так.' ;---освобождение памяти (ES имеет зна•1ение при старте) . MOV BX,ZSEG MOV AX,ES SUB ВХ,АХ MOV АН,4АН INT • 21Н ;получаем # параграфа ко1ща программы + ;получаем # параграфа на•~ала программы ;вычисляем размер программы в параграфах ;номер функции ;освобождаем память JC MEMORY ERROR ;проверяем на ошибку ;---пустой сегмент ZSEG SEGMENT ZSEG ENDS 1.3 .1 . Запуск одной программ~.~ нз другой MS-DOS обеспечивает функцию ЕХЕС (номер 4ВН прерывания 21Н), реализующую вызов одной программы из другой. Первая пРограмма называется "родителем", а загружаемая и запускаемая - "потомком". Высокий уровень В Бейсике вер<:ии 3.0 введена команда SHELL. Со значительными ограничениями она позволяет бейсиковской программе загрузить и выполнить другую программу. Формат этой команды - SHELL ком_строка. Командная строка может быть просто именем программы или она может содержать, кроме имени, параметры, которые обычно следую,: за именем программы в командной строке. Если ком_строка не указана, то загружается копия COMMAND.COM и появляется запрос операционной системы..В этот момент можно выполнить любую команду MS- DOS, а по завершении вернуть управление бейсиковской программе, введя команду EXIT. . Имеется ряд ограничений при использовании SHELL. Если загружаемая программа меняет режим работы дисплея, то он не будет автоматически восстановлен при возврате. Перед загрузкой . ПJ)Рграммы все файлы ,щ"1жны быть закрыты. Программа, которая остается резидент~ой после завершения, не может быть запущена
48 Глава l • командой SHELL. Обсуждение ряда других проблем содержится в руководстве по Бейсику. Средний уровень Применение функции 4ВН более сложно, чем применение остальных, оно требует четырех подготовител1,ных шаrов. 1. Подготовить в памяти место; доступное программе. 2. Создать блок параметров. 3. Построить строку, ~одержащую накопитель, путь и имя программы. 4. Сохранить значения регистров SS и SP в переменных. Поскольку при загрузке программы MS-DOS выделяет ей всю доступную память, необходимо освободить место в памяти. Если не освободить часть памяти, то не будет места для загрузки второй программы. В [l.3 .1] объяснено, как это сделать с помощью функции SETBLOCK. После -rого как память освобождена, вы должны просто поместить в ВХ требуемое число 16-байтовых параграфов, заслать 4АН в АН и выполнить прерывание 21Н, делая доступным программе именно то количество параграфов, которое ей необходимо. Блок параметров, на который должны указывать ES:BX, это 14-байтовый блок памяти, в который вы должны поместить следующую информацию: DW сегме11т111,1й адреL строки срет,I DD се1·мент и смещешIс комшщ1юй с-, рок11 DD сегмент и смещсш1с IIер110I·0 1:св DD сегмент ~1 смсIII_с11ие 1поро1·0 ГСВ , Строка среды - это строка, состоящая из одной или более спецификаций, которым следует MS-DOS при выполнении программы. Элементы строки среды такие же, как и те, что можно обнаружить в дисковом файле CONFIG.SYS. Например, в строку может быть помещено VERIFY = ON. Начните строку с первого элемента, завершив его символом ASCII О, потом запишите следующий и т.д. За последним элементом должны следовать два символа ASCII О. Строка должна начинаться на границе параграфа (т.е. се адрес по модулю 16 должен быть равен нулю). Это вызвано тем, что соответствующий вход в блоке параметров. указывающий на строку, содержит только 2-байтовос сегментное значение. Все это не нужно, если новая программа может работать с той же строкой среды, что и программа "родитель". В этом
Системные ресурсы 49 CJiyчae надо просто поместить два символа ASCII О в первые 2 байта блока параметров. Следующие 4 байта блока параметров указывают на командную строку для загружаемой программы. "Командная строка" - это символьная строка, определяющая способ работы программы. При загрузке программы из DOS она может иметь вид вроде EDITOR A:CHAPTER 1\ NOTES.MS. При этом вызывается редактор и ему передается имя файла в подкаталоге накопителя А для немедленного открытия. Когда вы подготавливаете командную строку для ЕХЕС, надо включать только последнюю часть информации, но не имя загружаемой программы. Перед командной строкой должен стоять байт, содержащий длину этой строки, и она· должна завершаться символом «CR,> (ASCII 13). Последние 8 байт блока параметров указывают на управляющие блоки файлов (FCB). FCB содержит информацию об одном или двух файлах, указанных в командной строке. Если открываемых файлов нет, то надо заполнить все 8 байт символом ASCII -0. В f5.3 .5] объяснено, как работает FCB. Начиная с версии MS-DOS 2.0, использование FCB необязатет,но и вы можете не включать информацию FCB, вместо этого используя новую конвенцию дескриптора файлов (filc handle), в которой доступ к файлу предоставляется по кодовому номеру, а нс через FCB (см.также [5.3 .5)). Наконец, вы должны построит~, строку с указанием накопителя, пути и имени файла. , Эта строка именует ' загружаемую программу. DS:DX указывает на нее при выполнении ЕХЕС. Эта строка - стандартная строка ASCIIZ, т.е. стандартная спеы,ификация файла, завершаемая кодом ASCII О. Например, это может быть B:\NEWDATA\FILER.EXE<<NUL>>, где символом «NUL,> обозначен код ASCII О. После того как вся. указанная информация подготовлена, остается последняя задача. Поскольку все регистры будут изменены вызываемой задачей, надо сохранить сегмент стека и указатель стека, с тем чтобы они могли быт1, восстановлены, когда управление будет возвращено вызвавшей задаче. Для их сохранения создайте переменные. Поскот,ку значение регистра DS также будет изменено, эти переменные нt: могут быть найдены, до тех пор пока DS не будет восстановлен операторами MOV AX,DSEG и MOV DS,AX. После того как SS и SP запомнены, поместите О в AL для выбора операции "загрузка и запуск" (ЕХЕС используется также для оверлее.в 11.3 .5 ]) . Затем поместите 4АН в АН и вызовите прерывание 21Н. В этот момент запущены. две программы, причем программа '"родитель" находится в остановленном· состоянии. MS-DOS предоставляет возможность • программе "потомок" передать "родителю" код возврата, таким
50 Глава 1 образом, могут быть переданы ошибки и статус. В [7.2 .5) объяснено, как это сделать. Что касается самой функции запуска, то при возникновении ошибки устанавливается флаг переноса, а регистр АХ в этом CJlyчae будет возвращать I для неправильного номера функции, 2 - если файл нс найден, 5 - при дисковой ош11бкс, 8 - при нехватке памяти, 10 - если нсправилr,на строка среды и 11 - если неверен формат. Приводимый пример - простейший из возможных, но часто большего для процедуры ЕХЕС и не требуется. Здесь оставлен нулевым блок параметров и не создана строка среды. Это означает, что загружаемой программе нс передастся командная строка и что среда будет такой же, как 11 для вызывающей программы. Вы должны только изменить распределение памяти, создать. имя и (пустой) блок параметров и сохранить значения SS и SP. ;---в сеrме1пе да11ных FILENAME D_B 'A:TRIAL.EXE ' , 0 ;з.1rружаем TRIЛJ,.EXE PARAMETERS DW 7DUPIO) ;11уле1юй блок II:Iраметро11 КЕЕР SS КЕЕР SP DWO DWО ;---перераспределение памяти MOV BX,ZSEG MOV ЛХ,ЕS SUB ВХ,АХ MOV АН,4АН INT 21Н ;---указываем на блок параметров ;I1еремеI11ннI ;uIя SS ;переме11ная ;~ля SI' ;получтъ # 11ара1·рафа кошщ :rIолу 1 1J.пI. # 11ара~·рафа tIачалn ;111,I•rf~слип, размер IIроI·раммI,I ;номер фу11кцш1 ;11срераспрсдслеI1fIс MOV AX,SEG PARAMETERS ;в ES - сеI·меIп МОУ ES,AX ,, MOV BX,OFFSET PARAMETERS ;в ВХ - смещеш,е ;---сохранить копии SS ft SP MOV КЕЕР _SS ,SS ;сохраняем SS МОУ КЕЕР_SP,SP ;сохраIIяем SP ;•--указываем на строку имени файла MOV DX,OFFSET FILENAME ;смсщсшIе - 11 DX MOV AX,SEG FILENAME МОУ DS,AX ;---загрузка программы MOV АН,4ВН MOV AL,O ;се,·мснт - 11 DS ;функцня ЕХЕС ;выбираем "загрузку fl з:шу~к"
Системные JJecypcы INT 21Н ;---впоследствии, восстанаwшпаем рс,·истры MOV AX,DSEG MOV DS,AX ;11осста11а1u1~1щ1см DS MOV SS,KEEP_SS ;восста11авли11аем SS MOV SP ,КЕЕР_SP ;восстанавливаем SP ;---в конце программы создаем фикпшный сегме11т ZSEG SEGMENT ZSEG ENDS ;см. [1.3.1] 1.3.3. Использование команд операционной системы из проrраммы 51 Программа может иметь в своем распоряжении полный набор команд интерфейса с пользователем DOS, таких, как DIR или 1 CHKDSK. Когда эти команды используются из программы, загружается и запускается вторая копия COMMAND.COM. Хотя такой подход при программировании может сэкономить много усилий, его успешная. реализация требует достаточного количества памяти для второй копии и ваша программа может .11опасть в "ловушку", если памяти недостаточно. Высокий уровень Бейсик 3.0 позволяет загрузить вторую копию COMMAND.COM с. помощью оператора SHELL. SHELL обсуждается в [1.3.2 ]. COMMAND.COM загружается, когда не указано имя файла, поэтому, вводя просто SHELL, вы получаете запрос MS- DOS. В этот момент можно использовать любую из утилит DOS, включая командные файлж. Для возврата в вызвавшую программу надо ввести EXIT. Средний уровень В этом случае к примеру, приведенному в 11.3 .2 J, нужно добавить командную строку. Обычно она начинается с байта длины строки, затем следует сама командная строка и, наконец, код ASCII 13. При передаче команды COMMAND.COM вы должны указать /С перед с,трокой (см. 'раздел "Вызов вторичного командного процессора" руководства по MS- DOS). Вы должны также указать накопитель, на котором находится COMМAND.COM, поместив имя накопителя, в начале командной'1 ,,.
52 Глава 1 строки. Чтобы вывести каталог накопителя А:, а COMMAND.COM при этом находится на накопителе В:, нужна строУ.а: COMMAND_LINE DB 12,'В: /С DIR л:·,13 Следующий кусочек кода устанавливает адрес командной строки в блок параметров, используемый в примере [ 1.3.2 ]: LEA BX,PARAMETERS ;rюлучеш,е адреса блока пар-1ю11 MOV AX,OFFSET COMMAND_LJNE ;11олу•1еш1с смсщс11ш1 командной строки MOV [ВХ] + 2,АХ ;пересылка 11 1-е 2 байта блока MOV AX,SEG COMMAND LINE MOV [ВХ] + 4,АХ ;nолучеш1е се1·ме1па командной строки ;пересылка во 2-е 2 байта блока 1.3 .4 . Сохраненне проrраммы в памятн после заверwення Программы, оставленные резидентными в памяти, могут служить в качестве утилит для других программ. Обычно такие программы вызываются через неиспользуемый вектор прерывания. MS-DOS рассматривает так\{е программы как часть операционной системы, защищая их от наложения других программ, которые будут загружены впоследствии. Резидентные программы обычно пишутся в форме СОМ, что обсуждается в ( 1.3.6 ]. Программы, написанные в форме ЕХЕ, оставить резидентными в памяти немного труднее. Завершение программы прерыванием 27Н оставляет ее резидентной в памяти. CS должен указывать на начало PSP для того, чтобы эта функция работала правильно. В программах СОМ CS сразу устанавливается соответствующим образом, поэтому надо просто завершить программу прерыванием 21Н. В проrраммах ЕХЕ CS первоначально указывает на первый байт, следующий за PSP (т.е. l00H). При нормальном завершении ЕХЕ-проrраммы последняя инструкция RET выталкивает из стека первые положенные туда значения: PUSH DX / MOV АХ,О / PUSH АХ. Поскольку DS первоначально указывает на начало PSP, при получении этих значений из стека счетчик команд указывает на смещение О в PSP, где при инициализации записывается инструкция INT 20Н. Поэтому INT 20Н выполняется, а . это стандартная функция для завершения программы и передачи управления в DOS. На рис. 1.5 показан этот процесс. Чтобы заставить прерывание 27Н работать в ЕХЕ.;;программе, надо поместить 27Н во второй байт PSP (первый содержит машинный код инструкции INT), а затем завершить программу обычным
•Системные ресурсы 53 RET. Для обоих типов файлов, прежде чем выполнить прерывание 27Н, DX должен С(jдержать смещение конца программы, отсчитываемое от начала PSP. Выnопн11еТСJ1 прерыеанме 20Н 11 уnра.,пенме IIOЗ8paЩaeТCJI DOS 8'18110 н " 8MIIТII lPSP s "' i Программа .,,- ... ( Стек ~ (CS:IP) IIJIIIHIIMIJOT ЭTII SН8'1eHIUI Ynl)88Jlettll8 nepqaeтCII C:IOAI :r: ,,. : • "' ... о ]:~ о~ а Рис. 1.5 . Завершение ЕХЕ-nроrраммы Средний уровень Конец М11Т11 na Вектор прерывания у~анавливается с помощью функции 2SH прерывания 21Н, как показано в (1.2.3 J (здесь используется вектор 70Н). Позаботьтесь, чтобы процедура. оканчивалась IRET. Кроме самой процедуры, устанавливаемая программа не должна делать ничеrо, помимо инициализации вектора прерыван~я, .присвоения DX значения смещения конца процедуры и завершения. Для СОМ-файлов просто поместите оператор INT 27Н в конец программы. Для ЕХЕ-файлов . поместите • этот оператор в первое слово PSP и завершите программу обычным оператором RET. Для тоrо чтобы выполнить процедуру, впоследствии загруженная программа должна вызвать INT 70Н. _ 1 Ниже приведены· примеры для обоих типов· файлов (СОМ и ЕЛЕ). В них устаиомена метка FINISH для отметки конца процедур"'- прерывания (напоминаем, что знак $ дает значение счетчика команд в этой точке). дilя СОМ-файлов FINISH дэе-r смещение от начала PSP, как и требуется для прерывания 27Н.
54 Глава l Для ЕХЕ-файлов смещение отсчитывается от первого байта, следующего за PSP, поэтому к нему необходимо прибавить l00H, чтобы пересчитать на начало PSP. Заметим, что, поместив процедуру в начало программы, мы можем исключить установочную часть кода из резидентной порции. Другой возможный фокус состоит в использовании инструкции MOVSB для пересылки кода процедуры вниз в неиспользуемую часть PSP, начиная со смещения бОН, что освобождает 160 байт памяти. Файл СОМ: ;---здесь процедура прерывания BEGIN: JMP SHORT SET_UP ROUТINE PROC FAR PUSH DS (процедура) РОР DS IRET FINISH EQU $ ROUТINE ENDP ;---установка вектора прерывания ;переход на уста1ю11ку ;сохра11ение регистров ;восстановление рс~iстров ;воз11рат ~13 прерываш1я ;отметка ко11ца 11ро1tсдуры SET_UP: MOV DX,OFFSET ROUTINE. ;смещсш,с нроцедуры u DX MOV AL, 70Н ;номер 11ектора прерыш1ш1я MOV АН,251-1 ;функция устшю11к~1 вектора INT 21Н ;усташ11~.rш1~.1см 11сктор ;---завершение программы, остаuJ1яя рсзидеtmюй LEA DX,FINISH ;011рс11еш1см требуемое смещеш1е INT 27Н ;завершение Файл ЕХЕ: ;---здесь резидентная процедура JMP SHORT SET UP ROUТINE PROC FAR PUSH DS (процедура) РОР DS IRET FINISH: EQU $ ROUТINE ENDP ;---установка вектора прерывания ;псрсхон 1ш уста11овку ;сохршrсш1е реr~1стро11 ;nоссташJ11леш1е реr~1стро11 . ;11О31 1рат ~13 11рсрывашrя ;отметка ко1ща процедуры
Системные ресурсы· SET_UP: MOV MOV моv моv MOV INT DX,OFFSET RОUТINЕ;смещение процедуры в DX AX,SEG ROUTINE ;сегмент процедуры в.DS DS,AX AL,70H АН,25Н 2111 ;номер вектор.1 преры11а1111я ;фу11кцн11 установки вектора. ;---запершеш1с программы MOV DX,FINISH + !ООН ;пычисJJяем смеще•~ие конца MOV ВУТЕ PTR ES:l,27H ;посыдаем 27Н в PSP RET ;завершаем процедуру 55 Функция 31Н прерывания 21 Н работает ана,,огично, за исключением того, что в DX должно содержаться число 16- байтов~х параграфов, требуемых процедуре (вычисление размера процедуры от начала PSP, см. в примере .из [1.3.1 ]). Преимуществом этой функции является то, что она передает родительской программе код выхода, дающий информацкю о статусе процедуры. Родительская программа получает этот код с помощью функции • 4DH прерывания 21 Н. Коды выхода обсуждаются в [7 .2 .5 ]. 1.3 .S. Заrрузка н запуск проrраммных оверnеев Оверлеи - это части програ~мы, которые остаются на диске, в то время как тело программы резидентно в паr,1яти. Когда требуется функция, выполняемая каким-либо оверлеем, он загружается в память и программа вызывает его как процедуру. Различные оверлеи могут загружаться в одно и то же место памяти, перекрывая предыдущий код. Например, программа ведения базы данных может загрузить процедуру сортировки, а затем перекрыть ее процедурой генерации отчетов. Эта техника применяется для экономии памяти. Но она хороша только для тех процедур; которые не используются 'постоянно, иначе частые обращения к диску приведут к тому, что программа будет выполняться слишком медленно. Средний уровень MS-DOS использует функцию ЕХЕС для загрузки оверлеев. Эта функция, номер 4ВН прерывания 21Н, служит также для загрузки и запуска одной программы из другой, если поместить код О в AL [1.3 .2 ]. Если в AL поместить код 3, то будет загружен оверлей. В этом случае не создается PSP, поэтому оверлей как
56 Глава 1 независцмая программа не устанавливается. Такая процедура просто загружает оверлей, не передавая ему управления. Существуют два способа обеспечить память для оверлея. Может быть использована либо область внутри тела программы, либо специально отведенная область памяти за пределами . головной программы. В качестве позиции функции ЕХЕС передается только сегментный адрес, по которому будет загружен оверлей. Когда оверлей загружается в тело головной программы, программа сама должна вычислить номер параграфа, куда будет загружаться оверлей. С друrой стороны, при загрузке в специально отведенную· память MS-DOS обеспечивает программу номером параграфа. В приведенном ниже примере используется загрузка в отведенную память. Поскольку DOS отводит программе всю доступную память, сначала необходимо освободить память с помощью функции 4АН. Функция 48Н отводит достаточно большой блок памяти, чтобы он мог принять самый большой из оверлеев. Эта функция возвращает значение сегмента блока в АХ, и этот номер параграфа определяет, куда будет загружен оверлей, а также по какому адресу оверлей будет вызываться головной программой. Эти функции детально обсуждаются в [1.3 .1 ]. Кроме кода 3, засылаемого в AL, вы должны установить для этой функции еще два параметра. DS:DX должны указывать на строку, дающую путь к файлу оверлея, которая завершается байтом ASCII О. Необходимо указывать пол!fое имя файла, включая расширение .СОМ или .ЕХЕ, поскольку DOS в данном случае не считает, что он ищет прОf'раммный файл. Наконец, ES:BX должны указывать на 4-байтовый • блок параметров, который содержит 2-байтовый номер параграфа, куда будет загружаться оверлей и 2-байтовый фактор привязки адресов в оверлее (привязка объясняется в [1.3.6 ]>. В качестве номера параграфа надо использовать число, возвращаемое в • АХ, для номера параграфа отведенною блока памяти. Фактор привязки дает смещение, по которому. могут быть вы•1ислсны адреса требующих привязки параметров в оверлее. Испот,эуйте номер ' параграфа, куда загружается оверлей. После того как он установлен, вызовите функцию и оверлей будет загружен. Изменяя путь к оверлейному файлу, можно внов1, и вновь вызывать. эту функцию, загружая вес новые и новые оверлеи. Если при возврате установлен флаг переноса, то была ош~1бка и се код будет возвращен в АХ. Код равен 1, если указан неверный номер функции, 2 - если файл нс найден, 5 - при дисковых ошибках и 8 - при отсутствии достаточной памяти. После того как оверлей загружен в памяп,, к нему можно получить доступ как к далекой (far) процедуре. В сегменте данных
Системные ресурсы 51 должен быть установлен двухсловный указатель, определяющий этот вызов. Сегментная часть указателя равна текущему кодовому сегменту. Смещение оверлея дол_жно быть вычислено нахождением разницы между сегментами кода и оверлея и умножени~м результата на 16 (при переводе величины из параграфов в байты). В приведенном примере две переменные OVERLAУ OFFSET и CODE SEG помещены одна за другой для правильной установки. указателя. Однажды загруженный оверлей затем может вызываться инструкцией CALL DWORD PTR OVERLAY_OFFSET. Оверлей может быть полной программой со своими сегментами данных и стека, хотя, как правило, используется стековый сегмент вызывающей программы. При вызове оверлея значение сегыента. его собственного сегмента данных должно быть помещено в DS. ;---завершаем программу фиктивным сегментом (см. (1.3.1)) ZSEG SEGMENT ZSEG ENDS ;---в сегменте данных OVERLAY SEG DW? OVERLAY_OFFSETDW ? ;смещение оверлея CODE_SEG DW? ;сегмент оверлея - должен РАТИ DB ·л:OVERLAY.EXE" ;следовать за смещением 0BLOCK DDО ;---освобождаем память MOV CODE_SEG,CS ·M OV AX,ES MOV BX,ZSEG ;4-байтовый блок параметров ;создаем копию CS ;кош1руем значение сегмента PSP ;адрес сегмента конца программы • SUB ВХ,АХ MOV АН,4АН INT 2IН ·;вычисляем.разность JC SВ.ТВLK_ERR ;---отводим память для оверлея ;номер функции SETBLOCK ;освобождаем память ;флаг переноса говорит об ошибке MOV BX,lOOH ;отводим для оверлея IOOOH байт MOV АН,481:1 INT 21Н JC ALLOCЛTION ERR MOV OVERl,ЛY_SEG,AX ;функция отведения памяти ;теперь АХ:О указывает на блок . ;флаг переноса говорит об ошибке ;запасаем адрес сегмента оверлея ;---вычисле11ие см~щения оверлея в кодовом сегменте MOV AX,CODE_SEG ;вычитаем значение сегмента оверлея MOV BX,OVERLAY)ШG ;из значения сегмента кода SUB ВХ,АХ ;ВХ содержит число параграфов
58 MOV CL,4 SHL BX,CL Глава 1 ;сдвигаем это число на 4 бита мево ;чтобы получить величину в байтах MOV OVERLAY_OFFSET,BX ;запоминаем смещение ;---загрузка первого оверлея MOV AX,SEG BLOCK MOV ES,AX MOV BX,OFFSET BLOCK моv AX,OVERLAY_SEG моv [ВХ],АХ MOV [ВХ] + 2,АХ LEA DX,PATH моv AH,48i-I MOV AL,3 INT 21Н ;ЕS:ВХ указывает на блок параметров ;помещаем адрес сегмента оверлея в ;первое слово блока параметров ;сегмент оверлея - фактор привязки ;DS:DX указывает на путь к файлу ;номер функции ЕХЕС ;код загрузки оверлея ;загружаем оверлей JC LOAD ERROR ;флаг переноса говорит об ошибке ;---теперь программа занимается своими делами CALL DWORD РТR OVERLAУ_OFFSET ;вызов оверлея ;нужно указывать· DWORD РТR, так как ;оверлей - далекая процедура ;---посмотрите эту структуру, когда будете писать оверлей DSEG SEGMENT ;как об"1чно, устанавливаем ~;егмен:r данных ;опускаем стековый сегмент (используется ;стек вызывающей программы) ENDS DSEG CSEG SEGMENT PARA PUBUC ·coDE' OVERLAYPROC FAR ;всегда далекая процедура ASSUME CS:CSEG,DS:DSEG. PUSH ;храним DS вызывающей программы MOV , AX,DSEG ;устанавливаем DS оверлея MOV DS,AX . РОР RET OVERLAYENDP · CSEG ENDS END DS ;восстанавливаем DS при завершении ,.
Системные ресурсы 59 1.3 .6 . Преобразованне проrрамм нз тнпа .ЕХЕ в тнп .СОМ Программисты на ассемблере имеют возможность преобразовать свои программы из обычного формата ЕХЕ в формат СОМ. Файлы ЕХЕ имеют заголовок, . содержащий информацию для привязки; DOS привязывает некоторые адреса программы при загрузке. С другой стороны, файлы СОМ существуют в таком виде, что привязки не требуется, они хранятся уже в том виде, ·в котором загру:жаещ1я программа должна быть в памяти машины. По этой причине файлы ЕХЕ по меньшей мере на 768 байт больше на диске, чем их СОМ­ эквиваленты (хотя при' загрузке в память они будут занимать одинаковое место). Файлы СОМ также быстрее загружаются, поскольку не требуется привязки. Других преимуществ у них нет, а некоторые программы слишком сложны и слишком велики, чтобы их можно было преобразовать в тип СОМ. Привязка - это прqцесс установки адресов. .связанных с сегментным регистром. Например, программа может указывать на начало области данных следующим 'кодом: • MOV DX,OFFSET DATA AREA t - MOV AX,SEG DATA_AREA MOV DS,AX Смещение в DX связано с установкой сегментн()rо регистра DS. Но какое значение должен принимать сам· DS'? Программа требует абсолютный адрес, • НО номер параграфа, в котором будет располагаться DATA AREA, зависит от того, в какое место в памяти будет загружена программа, а это зависит от версии · MS-DOS и от того, какие резидентные программы будут находиться в младших адресах памяти. По этой причине во время компоновки программы можно только установить некоторые сегментные значения через смещения относительно начала программы. Затем, когда DOS осуществляет . привязку,. значение началь,ноrо адреса программы прибавляется к сегментным значениям, давая абсолютные адреса, требуемые в сегментном регистре. На рис. 1.6 показан процесс привязки. Файлы СОМ не нуждаются в привязке, поскольку они хранятся в таком виде, что им не требуется фиксация· сегмента. Все в программе хранится относительно начала кодового сегмента, включая все данные и стек. По этой причине· программа не может прещ,1шать 65535 байт по длине, что соответствует максимальному смещению, которое существует в используе~ой схеме адресации (поскольку верхняя часть этого блока занята стеком, реальное
60 Глава 1 пространство, доступное для кода и данных, немного меньше, чем 65535 байт, хотя стековый сегмент при необходимости может быть вынесен за границу 64-килобайтового блока). В файлах СОМ все сегментные регистры указывают на начало PSP; сравните с файлами ЕХЕ, где DS и ES инициализируются аналогичным образом, но CS указывает на пер~ый байт, ~едующий за PSP. Прм nрмв11ЭКе добавn11ете11 два зна~енм11 (= 1421Н параграфов) 005 помещает СЮАЗ зна~нме SEG DAТA_AREA 1 1ОООН параграфов до начала программы 421Н параграфоl 1 1 дo"DATA_AREA" 1 • ·1 Программа 0000:0000 1000:0000 Рис. 1.6 . Привязка значе11нй памяти DATA_AREA Для представления программы в виде· файла СОМ требуется соблюдение следующнх правил: 1. Не оформляйте программу в виде процедуры. Вместо этого поместите в самое начало метку, вроде START, и завершите программу оператором END START. 2. Поместите в начале программы оператор ORG lO0H. Этот оператор указывает начало .кода (т.е. устанавливает счетчик команд). Программы СОМ начинаются с !ООН, что является первым байтом, следующим за PSP, поскольку CS указывает на начало PSP, которое расположено на I00H байт ниже. Для того чтобы начать выполнение с любого другого места, поместите по адресу l00H инструкцию JMP. 3. Оператор ASSUME должен устанавливать DS, ES и SS таким образом, чтобы они совпадали со значением для кодового сегмента, например ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG.
Системные ресурсы 61 4. Данные программы могут помещаться в любом месте программы до тех пор, пока они не перемешаны с кодом. Лучше начинать программы с области данных, поскольку макроассемблер может выдавать сообщения об ошибках при первом проходе, если имеются ссылки на идентификатор данных, который еще не обнаружен. Для перехода к началу кода используйте в качестве первой команды программы инструкцию JMP. 5. Нельзя применять фиксацию сегментов типа MOV AX,SEG NEW DAТА. Достаточно указания одного смещения метки. В частности, нужно опускать обычный код, используемый в начале программы для устано&ки сегмента данщ,1х, MOV AX,DSEG / MOV DS,AX. • 6. Стековый сегмент полностью опускается в начальном коде. Указатель стека инициализируется на вершину адресного пространства 64К, используемого программой (напоминаем, что стек растет вниз в памяти). В программах СОМ он должен быть сделан меньше, чем, 64К, SS и SP могут быть изменены. Имейте в виду, что при компоновке прог}h1ммы компоновщик • выдаст сообще,ше об ошибке, указывающее, что сегмент стека отсутствует. Игнорируйте его. 7. Завершите программу либо инструкцией RET, либо прерыванием 20Н. Прерывание 20Н - это стандартная функция для завершения программы и возврата управления в DOS. Даже когда программа завершается инструкцией R ЕТ, на ~1мом деле используется прерывание 20Н. Это происходит потому, что вершина стека первоначально • содержит О. При выполнении завершающей инструкции программы RET О выталкивается из стека, переназначая счетчик команд на начало PSP. Находящаяся в этой ячейке функция 20Н выпол11яется как следующая инструкция программы, вызывая передачу управления в DOS. Все это означает, что вам не надо при старте программы помещать на стекDSиО(PUSHDS/MOVАХ,О/PUSHАХ>,какэто требуется для ЕХЕ-файлов. . После того как программа сконструирована таким образом, ас­ семблируйте и компонуйте ее как обычно. Затем преобразуйте се в форму СОМ с помощью утилиты EXE2BIN, имеющейся в MS- DOS. Если имя • программ~, . построенной компоновщиком, MYPROG.EXE, то просто введите команду EXE2BIN MYPROG. В результате вы получите программ1-1ый файл • с именем MYPROG.BIN. , Все что вам останется теперь сделать, переименовать этот файл в MYPROG.COM. Вьi можете также сразу использовать команду EXE2BIN MYPROG MYPROG.COM для получения файла с расширением СОМ.
62 Глава l Низкий уровень В • данном примере содержится законченная короткая программа, которая по установке переключателей определяет количество накопителей в машине и затем выводит сообщение на экран. Она может служить примером короткой утилиты, для которой формат СОМ идеален. ' CSEG ;---данные SEGMENT ORG lOOH ASSUME CS:CSEG, DS:CSEG, SS:CSEG START: JMP SHORT 8EG1N ;переход к коду MESSAGEl D8 ·тье dip switches are set ror $" MESSAGE2 D8 "disk drive(s).$" ;---печать первой части сообщения 8EGIN: ,MOV АН,9 ;функция 9. прерывания 2\Н - вывод MOV DX,OFFSET MESSAGEI ;строки INT 21Н ;пыводим строку PUSH АХ ;сохраняем номер функции на будущее ;---получаем установку переключателей из порта А микросхемы 8255 IN AL,61H ;получаем байт нз порта В OR AL, 100000008 ;устанаwшваем б•п 7 OUT 61H,AL ;заменяем байт IN AL,60H ;nолу'lаем установку переклю'lателей .,, AND AL. 110000008 ;выделяем стар1ш1е 2 бита MOV CL,6 ;11од1хпо11ка к сд1~н1·у ЛI, 1111ра110 SHR ЛL,CL ЛDD ЛL,49 MOV DL.ЛL MOV АL,6\Н AND AL,0111111 \В OUT 61Н,АL ;сд11и1·аем 2 б1-1п1 в 1~а 1 1ало ;доба11лясм t. чтобы с•штап, с 1 ;•1 48 для nсре1юда 11 ЛSCII ;номсщасм резул1,тат в DI, ;д0JIЖlll,1 IKJCCTШIOIIИTb норт 8 ;сбрасываем б•п 7 ;11озвращаем байт ;---печать числа накопителей • MOV АН,2 ;функция 2 nрерываш1я 21 Н INT 21Н ;nе•1атаем 'IИCJIO из DL ;---печать второй половины сообщения CSEG РОР АХ ;берем номер фу11кцш1, со стека MOV DX,Ol'FSET MESSЛGE2 INT 2\Н INT 20Н ENDS END STЛRT ;ш,11юдим строку ;за11ершсш1е про1·рамм1,1
Глава 2. Тай меры и звук 2.1. Установка н чтение таймера Вес IВМ РС используют микросхему таймср,t 8253 (или 8254) для согласования импульсов от микросхемы сис­ темных часов. Число циклов системных часов преобразуется в один импульс, а последовательность ·этих импульсов подсчитыва­ ется для определения времени или они могут быть посланы на громкоговоритель компьютера для генерации звука определенной частоты. Микросхема 8253 имеет три идентичных н~зависимых канала, каждый-из которых может программироваться. Микросхема 8253 работает цезависимо от процессора. Про~ цессор программирует микросхему и затем обращается к другим делам. Таким образом, микросхема 8253 действует как часы реального времени - она считает свои импульсы независимо от того, что происходит в компьютере. Однако максимальный программируемый интервал составляет приблизительно l / 12 с. Для подсчета интервалов времени в часы и минуты нужны какие­ то другие средства. Именно по этой причине импульсы от нулевого канала микросхемы таймера накапливаются в переменной, находящейся в области даннь,.х BIOS. Этот процесс показан на рис. 2.1. Такое накопление обычно называется подсчетом· времени суток. 18,2 раза в секунду выход канала О обрабатывается аппаратным пр~рыванием (прерыванием таймера), которое ненадолго останавливает процессор и увеличивает показат1я счетчика времени суток. Число· О соответствует полночи 12:00; когда счетчик достигает значения, эквивалентного 24 часам·, он· сбрасывается на ноль. Другое время в течение суток легко опре­ деляется делением показателя счетчика на 18,2 для каждой .
64 Глава 2 секунды. Счетчик времени суток используется в большинстве операций, связанных со временем. Микросхема таймера Микросхема прерываний 8253 8259 Системные Канал О Вызывает часы Считает до 65535 INT8 и выдает импульсы . Область данных BIOS ROM-BIOS Увеличиваетс11 4-байтовый Выполн11ет счетчик INТ8 в0040:006С Рис. 2.1 . Изменение счет,шка време1111 суток BIOS 1.1.1. Проrраммнрованне мнкросхемы таймера 8153 /8154 Каждый из трех· каналов микросхемы таймера 8253 (8254 для АТ) ·состоит из трех регистров. Доступ к каждой группе из трех регистров осуществляется через один порт; номера портов от 40Н до 42Н соответствуют каналам О - 2. Порт связан с 8-битовым регистром ввода/вывода, который посылает и ·принимает данные для этого канала. Когда канал запрограммирован, через этот порт посылается 2-байтовое значение, сначала младший байт. Это число передается в 16-битовый регистр задвижки (\atch regis1cr), который хранит это число и из которого копия помещается в 16- битовый регистр счетчика. В регистре счетчика число уменьшается на единицу каждый раз, когда импульс _от системных часов пропускается через канал. Когда значение этого числа достигает нуля, канал выдает выходной сигнал и затем новая копия содержимого регистра задви.жки передвигается _в регистр счетчика, после чего процесс повторяется. Чем меньше •число в регистре счетчика, тем быстрее ритм. Все три канала всегда активны: процессор не включает и не выключает их.· Текущее значение любого из регистров счетчика может быть прочитано в любой .момент времени, что нс влияет на счет. ,1
Таймеры и звук 65 Каждый канал имеет две входные и одну выходную линии. выходная линия выводит импульсы, возникающие в результате подсчета. Назначение этих сигналов варьируется в зависимости от типа IВМ РС. ■ Канал О используется системными часами времени су;ок. Он устанавливается ВIOS при старте таким образом, что :оьщает импульсы'приблизительно 18,2 раза в секунду. 4-байтовый счет­ чик этих импульсов хранится в памяти по адресу 0040:ООбС (младший '5айт хранv.тся первым). Каждый импульс инициирует прерывание таймера (номер 8) и именно это прерывание увели­ чивает показание счетчика. Это аппаратное прерывание, поэтому оно обрабатывается всегда независимо от того, чем занят процес­ сор, если только разрешены аппаратные прерывания (см. [ 1.2.2 ]) . Выходная линия используется также для синхронизации неко­ торых дисковых операций; поэтому если вы изменили ее значение, то вам необходимо ВОС{:тановить первоначальное значение перед обращением к диску. ■ Канал 1 управляет обновл1;;ну,ем памяти ii.a зс1;;;.; ~:ашинах, кроме PCjr, поэтому его лучше не трогать. Выходная линия этого канала связана с микросхемой прямого доступа к памяти [5.4 .2 ], и ее импульс заставляет микросхему DMA обновить всю память. На PCjr канал 1 служит для преобразования входных данных с клави­ атуры из последовательной в параллельную форму. PCjr не использует микросхему прямоrq доступа к памяти, поэтому когда он вместо этого прогоняет данные через процессор, прерывание от таймера заблокировано. Канал I используется для подсчета заблокированных импульсов часов времени суток, с тем чтобы можно было обновить значение счетчика после завершения дисковых операций. 1 ■ Канал 2 связан с громкоговорителем 'компьютера и он произ­ водит простые прямоугрльные импул1,сы для генерации звука. Программисты имеют больше возможностей управлять вторым каналом, чем остальными. Простые звуки могут генерироваться одновременно с другими программными операциями, а более слож­ ные звуковые эффекты могут быть достигнуты за счет исполь­ зования процессора. Канал 2 может быть отсоединен от громко­ говорителя и служить для синхронизации. Наконец, выходная линия канала 2 связана с динамиком компьютера. Однако динамик не будет генерировать звук до определенных установок микро­ схемы интерфейса с периферией 8255. Две входные линии для каждого канала состоят из линии часов, передающей сигнал от микросхемы системных часов и • линии, называемой воротами (gate), которая включает и выклю­ чает сиrн~л от часов. Ворота всегда открыты для сигналов часов по каналам О и 1. Но они могут быть закрытыми для канала 2, что ·} 3 Р. Джордейн
66 Глава i позволяет производить некоторые специальные манипуляции со звуком. Ворота закрываются установкой младшего бита порта с адресом 61Н, который является регистром микросхемы 8255; сброс этого бита снова открывает ворота. Эта микросхема обсуждается в [l.l.l ]. Оrметим, что, как и выход канала 2, бит l порта 61Н связан с динамиком и также может использоваться для генерации звука. На рис. 2.2 приведена диаграмма микросхемы таймера 8253. Часы канало i--...,._ _ Задвмжка Счетчик Ворота {всегда открыты) Канал 1 1---+ -~ Задвижка Счетчик Ворота (всегда открыты) каtал2 Ворота 8255 Интерфейс с периферией ПортВ7б54Э21О Выходной сигнал ~ ----------- АИНамика Рис. 2.2 . Микросхема таймера 8253/8254 Прерывание таймера BIOS Обноепенме nам11ти Микросхема таймера может применяться непосредственно для временных операций, но это редко бывает удобным. Ввод с часов произ.водится 1,19318 млн. раз в секунду (даже на АТ, где систем­ ные часы идут быстрее, микросхема таймера получает сигнал с частотой 1, 19 МГц). Поскольку максимальное число, которое может храниться в 16 битах, равно 65535 и поскольку это число делится на частоту импульсов от часов, равную 18,2, то макси­ мальный возможный интервал между импульсами равен .приблизи-
Таймеры и звук 67 тельно 1/ 12 с. Цоэтому в большинстве временных операций используется счетчик времени суток BIOS. Для подсчета времени читается значение времени суток и сравнивается с некоторым ранее запомненным значением для определения числа импульсов, прошедших с того момент~. Специальный способ, описанный в [2.1 . 7 ], позволяет использовать счетчик времени суток для опе­ раций в реальном времени. Микросхема таймера 8253 предоставляет разработчикам обору­ дования 6 режимов работы для каждого канала. Программисты обычно ограничиваются третьим режимом как для канала О при синхронизации, так и для кащ1ла 2 при синхронизации или гене­ рации звука. В этом режиме, как только регистр задв1:1жки полу­ чает число, он немедленно загружает копию в регистр счетчика. Когда значение в счетчике достигает нуля, регистр задвижки мгно­ венно перезагружает счетчик и т.д. В течение первой половины отсчета выходная линия включена, а в течение второй - выклю­ чена. В результате получаются прямоугольные волны, которые одинаково пригодны как для генерации звука, так и для подсчета. 8-битовый командный регистр управляет способом загрузки чисел в канал. Адрес порта для этого регистра равен 43Н. Команд­ ному регистру передается байт, который сообщает, какой канал программировать, в каком режиме, а также один или оба байта регистра задвижки должны быть переданы. Он показывает также, будет ли число в двоичной или BCD (двоично-кодированной деся­ тичной) форме. Значение битов этого регистра таково: бит О 3-1 5-4 7-6 если О, двоичные данные, и11.1'1с ПСD номер реж~1ма, 1 - 5 <ООО - 1О 1) п111 011ер::щ~н1: 00 11ередать зщ1•1е11ие счетч~1ка II задвижку 01 •штать/писап, только стар1ш1й баi'п 10 •1итать/ш1сать тшн,ко младший байт 11 •штать/пж::ать старший байт, потом младший номер программируемого ка11ала, О - 2 <00 -10) Короче говоря, для программирования микросхемы 8253 надо выполнить три основных шага. После того как третий шаг завер­ шен, запрограммированный канал немедленно начинает функци­ онировать по новой программе. 1.. Послать в командный регистр (43Н) байт, представляющий цепочку битов, которые выбирают канал, статус чтения/записи, режим операции и форму представления чисел. 3*
68 Глава 2 2. Для канала 2 надо разрешить сигнал от часов, установив в 1 бит О порта с адресом 61Н. (Когда бит I этого регистра установлен в 1, канал 2 управляет динамиком. Сбросьте его в О для операций синхроН.\iЗации.) 3. Вычислить значение счетчика от О до 65535, поместить его в АХ и послать сначала младший, а з~1тем старший байт в регистр ввода/вывода канала (40Н - 42Н>. Каналы микросхемы 8253 работают постоянно. По этой причине программы всегда должны -восстанавливать начальJ{ые установки реmстров 8253 перед завершением. В частности, если при завершении программы генерируется звук, то он будет продолжаться даже после того, как MS-DOS получит управление и загрузит другую программу. Имейте это в виду- при написании процедуры выхода по «Ctrl-Break» [3.2.8 ]. 1 Низкий уровень В данном примере канал О программируется на другое значе­ ние, а не на уст~новленное BIOS при старте. Причина изменения установки состоит в том, чтобы изменить интервал изменения счетчика времени суток на величину, большую, чем 18,2 раза в секунду. Частота обновления счетчика изменяется, скажем, на 1000 раз в секунду с целью· проведения точных лабораторных измерений. Значение задвижки должно быть 1193 (1193180 тактов в секунду / 10000). Как читать текущее значение регистра счет­ чика см. в (2.1.8 ]. Перед дисковыми операциями оригинальное значение задвижки должно быть восстанQвлено, поскольку канал О используется для синхронизации дисковых операций. Максимально возможное значение - 65535 тактов часов междv импvльсами от канала - может быть достигнуто засылкой О в реr~стр задвижки <О немедленно превращается в 65535 при уменьшении на единицу). ;---установка регистров 1шода/ш,111ода СОМ REG EQU 43Н • ;адрес кома11д1ю1'0 реrщ:тра CHAN О EQU 40Н ;адрес канала О MOV AL.001I0II08 ;устшювка б~по11 для канала 2 OUT COMMAND_REG,AL ;з.'\сылка в командный регистр . ;--- посылка счетчика в задвижку MOV. АХ,II93 OUT CHANNEL_2,AL MOV AL,AH OUT CHANNEL_2,AL ;счетчик для 100 импульсов/с ;посылка младшего байта ;готовим для посылки старший байт ;посылка старше10 байта
Таймеры и звук 69 1.1.1 . Установка/чтение времени При старте MS-DOS запрашивает у пользователя текущее время. Введенное значение помещается в 4 байта, хранящие счет­ чик времени суток (начиная с 0040:ООбС, младший байт хранится первым). Но сначала оно преобразуется в форму, в которой под­ считывается время суток, т.е. время преобразуется в число восем­ надцатых долей секунды, прошедших с полуночи. Это число постоянно обновляется 18,2 раза в секунду прерыванием таймера. Когда появляется очередной запрос на время, текущее значение счетчика времени суток прообразуется обратно в привычный формат часы-минуты-се~унды. Если при старте не было введено знач~ние, то счетчик устанавливается в ноль, как будто сейчас полночь. Компьютеры, снабженные микросхемой календаря-часов, могут автоматически устанавливать счетчик времени суток. Высокий уровень TIME$ устанавливает или получает время в виде строки чч:мм:сс, где· часы меняются от О до 23, начиная с полуночи. Д11я 5:10 дня: НЮ ТIМЕ$ "17:10:00" ·установка времени 11О PRINТ TIME$ ·вывод времени Поскольку TIME$ возвращает строку, для выделения отдель­ ных частей показания часов можно использовать строковые функ­ ции MID$, LEFT$ и RIGHT$. Например, чтобы преобразовать время 17:10:00 в 5:10, вы должны вырезать строку символов, соот­ ветствующую часам, преобразовать ее в числовой вид (с помощью функции VAL), в·ычесть 12, а затем представить результат снова в виде строки: •100Т$=TIME$ 110 HOUR$ = LEFf$П$,2) ·получаем строку времеш1 выделяем з11аченне часов 120 MINUТES$ = М1D$П$,4,2) вы.деляем значение м~jнут 130 NEWHOUR = VAL(HOUR$) ·преобразуем •1асы в •1нсло 140 IF NEWHOUR > 12 THEN NEWHOUR = . N EWHOUR - 12 IS0 NEWHOUR$ = SТR$(NEWHOUR) ·,юное зна•1енне в строку 160 NEWТIME$ = NEWHOUR$ + ":" + MINUTES$ ·делаем 1ю11ую строку
70 Глава 2 Средний уровень MS-DOS предоставляет прерывания для чтения и установки времени, производя необходимые преобразования между значе­ нием счетчика времени суток и часами-минутами-секундами. Время выдается с точностью до 1/ 100 секунды, но поскольку счетчик времени суток обновляется с частотой в пять раз меньшей, показания сотых секунд очень приближенные'. Функция 2СН прерывания 21Н выдает время, а функция 2DH устанавливает его. В обоих случаях СН содержит часы (от О до 23, где О соответствует полночи), CL - минуты (от О до 59), DH - секунды (от О до 59) и DL - сотые доли секунд (от О до 99). Кроме того, при получении времени функцией 2СН AL содер­ жит номер дня недели (0 = воскресенье). Значение дня будет вер­ ным, тольк,Ь если была установлена дата. DOS вычисляет номер дня недели, по дате. ,Отметим также, что при установке времени функцией 2DH AL отмечает правильность введенного значения времени (0 = правищ,но, FF ,;,, неправильно). ;--Q"Становка времени моv CH,HOURS моv CL,MINUTES MOV DH,SECONDS MOV DL,HUNDREDTHS MOV AH,2DH INT 21Н СМР AH,0FFH JE ERROR ;---получение времени ;вuодим зна•1еш1я 11ремеш1 ;номер фувкцш1 уста11011кн 11рсмеш1 ;уста11а11шшаем 11ремя ;проверяем правнлыюсп, з11ачс1~ня ;переход на обработку ошибки MOV АН,2СН ;номер функции получения uрсмс11н INT 21Н ;получаем uремя MOV DAУ_OF_ WEEK .AH ;полу•~аем деш, 11едел~1 нз Лl-1 Низкий уровень Если вы изменили скорость импульсов канала I микросхемы 8253 для специальных приложений, то необходимо написать свою процедуру декодирования показаний счетчика времени суток. BIOS позволяет диапазон значений счетчика от О до 1,573 миллиона и это может быть изменено только путем изменения прерывания таймера. Поэтому часы, реально показывающие сотые доли _секунды, не могут работать 24 часа без специально написанной
Таймеры и звук 71 праграммы. Отметим также, что байт 0040:0070 устанавливается в ноль при старте, а затем увеличивается на 1 (не больше) по ходу часов. 1.1.З. Установка/чтение даты При включении компьютера MS-DOS запрашивает у пользо­ вателя текущие дату и время. Время записывается в области дан­ ных BIOS. Дата же содержится в переменной в COMMAND.COM. Она хранится в qюрмате трех последовательных байтов, которые содержат соответственно день месяца, номер месяца и номер года, начиная с О, где О соответствует 1980 rоду. В отличие от счетчика времени суток, адрес даты в пам~ти меняется с изменением версии DOS и положением в памяти COMMAND.COM. По этой причине для получения даты всегда надо использовать готовые утилиты Бейсика или MS-DOS, а не обращаться к этой переменной напрямую. Машины, оборудованные микросхемой календаря-часов, авто- / " матически устана,вливают время и дату с помощ1,ю специальнои программы (обычно запускаемой при старте через файл AUTOEXEC.BAT). Как получить доступ к микросхеме календаря­ часов см. в (2.1 .4 J. Отметим также, что коrда счетчик времени суток BIOS переходит через отметку 24 часов, MS-DOS меняет дату. Высокий уровень Оператор Бейсика DA ТЕ$ устанавливает или получает дату в виде строки qюрмата ММ-ДД-ГГГГ. Можно исnол~,зовать косую черту (/) вместо дефиса (-). Первые две цифры года моrут быть опущены. Для 31 октября 1984 r.: 100 DATE$ = "10/31/1!4"'усп111ш1ка даты 110 f>RINT DATE$ I1ыIюд даты .. , и на дисп лее будет вы вед еIю : 10-31-191!4. Средний уровень Функции 2АН и 2ВН прерывания 21Н получают и устанав­ ливают дату. Для получения даты поместите в АН 2АН и выпол­ ните прерывание. При возврате СХ будет содержать год в ви;:~с числа от О до 119, что соответствует диапазону лет 1980 - 2099
72 Глава 2 (можно сказать, что выдается смещение относительно 1980 rода). DH содержит номер месяца, а DL - день. MOV АН,2АН ;номер функции получения даты INT 21Н ;получение даты MOV DAY,DL ;день из QL MOV МОNТН,DН ;месяц из DH ADD СХ,1980 ;добавляем базу к году .MOV YEAR,CX ;получаем номер года Для установки даты поместите день, месяц и rод в те же регистры и выполните функцию 2ВН. Если значения, указанные для даты, неверны,, то в AL будет возвращено FF, в противном случае - о: • MOV DL,DAY ;щ~мещаем день·в DL MOV DH,MONTH ;помещаем месяц в DH моv CX,YEAR ;помещаем год в СХ SUB СХ,1980 ;берем смещение относительно 1980 MOV АН,2ВН ;номер функции установки даты INT 21Н ;установка даты СМР AH,0FFH ;проверяем успешность операции JE ERROR ;неверная дата, идем на обработку ошибки 1.1.4 . Установка/чтение часов реаnьноrо времени Часы реальною времени имеют свой собственный процессор, который может прдсчитывать время, не влияя на другие компью­ терные операции. Они имеют также независимый источник пита­ ния, используемый, когда компьютер выключен. Программно можно как читать, так и устанавливать часы реальною времени. Обычно имеется дополнительное программное обеспечеlfие, кото­ рое устанавливает счетчик времени суток BIOS и переменную даты DOS таким· образом, чтобы они соответствовали текущим показаниям часов реальною времени. Но можно программно про­ верить соответствие между ними и при обнаружении разногласий принять необходимые меры. • Различные установки времени и даты осуществляются через набор адресов портов. Многие многофункциональные платы "°"' расширения .для IВМ РС имеют часы реальною времени, но, к сожалению, нет стандартной микросхемы и диапазона адресов портов. АТ оборудуется часами реального времени, основанными на микросхеме МС146818 фирмы Motorola, котор~е используют те
Таймеры и звук 73 .же регистры, что и микросхема, содержащая данные о конфигурации системы. Доступ к этим регистрам можно получить, послав сна1:1ала номер требуемого регистра в порт 70Н, а затем прочитав значение регистра через порт 71 Н. Регистры, связанные с часами, следующие: Номе~ i!!<rист~а Фvнкц~,я ООН секу,щы ОIН секу1щш1я трс1ю1~1 O2Н мю1уты озн м~шутная трс1ю1·а O4Н часы OSH часоuая тpeuo1':I O6Н деш, недели O7Н день месяца O8Н месяц O9Н rод ОАН рег~1стр статуса А овн ре,·истр статуса В осн реп,стр статуса С оон ре1·истр статуса D Биты четырех статусных регистров функции, из которых интерес для представлять следующие: выполняют различные программистов могут Регистр А: бит 7 Регистр В: бит 6 бит 5 бит 4 бит 1 бит О 1 1 = идет модифи~ация времени (надо ждап, значения О, чтобы читать) = разрешено nериоди•1еское nреры11аш1е = разреше,ю nрерыва~ше тре11O1·~1 = разреше110 nрерьшаш,е ко1ща м<щ1,фик:щнн = часы с•,~паются по 24, ·о = :ю \2 = разрешено зш10мн11шшс 11рсмсш, суток Часы реального времени на АТ могут вызывать аппаратное прерывание IRQ8. Программа может установить вектор этого пре­ рывания ца любую процедуру, которую требуется выполнить в определенное время [1.2.З ]. Используйте вектор 4АН. Операции в реальном времер:и, производимые таким образом, менее хлопотны, чем обсуждаемые в [2.1. 7 ] (хотя и ценой компактносз-и программ).
74 Глава 2 Прерывание может вызываться одним из трех способов, каждый из которых запрещен при старте. Периодическое прерывание проис­ ходит через определенные интервалы времени. Периодичность приближенно равна одной миллисекунде. Прерывание тревоги про­ исходит, когда значение трех регистров тревоги совпадает со зна­ чениями соответствующих временных регистров. Прерывание конца модификации происходит после каждого обновления значе­ ний регистров микросхемы. Прерывание IAH расширено в BIOS АТ, чтобы оно позволяло читать и устанавливать часы реального времени. Поскольку по~.,, - зания никогда не состоят более чем из двух десятичных цифр, значения времени выдаются в двоично-кодированной десятичной форме (BCD), когда байт делится на две половины и каждая деся­ тичная цифра представляется четырьмя битами. Такой формат позволяет легко переводить числа в форму ASCII. Программе нужно только сдвинуть половину байта в младший конец регистра и добавить 48 для получения кода ASCII, соответствующего дан­ ному числу. Для всех IВМ РС функции О и 1 прерывания IAH читают и устанавливают счетчик времени суток BIOS. Для часов реального времени АТ имеется шесть новых функций. Функция 2: Чтение 11реме11и из часо11 рсал1,1ю1'0 времени При возврате: СН=часыIIBCD CL = ми11уты в BCD ОН = секунды II BCD Функция 3: Устано11ка времени •шсо11 реаль11ого 11рсмсш1 При входе: СН=,,асывBCD CL = мш,уты II JJCD ОН = секунды II BCD DL = if daylight savings, elsc 1 Функция 4: Чтение даты из •шсов реал1,1101'0 времени При возврате: СИ=веквBCD(19иш,20) CL=годвDCD(с1980) DH=месяцвBCD DL = день !Чесяца II BCD Функция 5: Установка даты часов реалыюго 11ремени При входе: СН=веквBCD(19ш,~,20) CL=годIIBCD(с19801 DH=месяцuDCD DL = де11ь месяца в BCD Функция 6: Установка трепоп, для •шсоu реалыюго 11ремсш1 При входе: СН=часывBCD CL = ми11уты II BCD DH = секунды u BCD Функция 7: Сброс тревоп, iнет 11ход11ых регистров)
Таймеры и звук 75 Тревога устанавливаеТ<:я как смещение относител~,но текущего момента времени. Максималью,1й период равен 23:59:59. Как уже rоворилось выше, вектор прерывания 4АН должен указывать на процедуру обработки тревоги. Отметим, что если часы не работают (наиболее вероятно, из-за отсутствuя питания), то выполнение функций 2, 4 и 6 устанавливает флаг переноса. 1.1 .5 . Задержка проrраммных операций Если вы осуществляете задержку в программе посредством пус­ того цикла, то вам может потребоваться много времени для того, чтобы добиться нужноrо времени задержки. Даже если вы оrrреде­ лите требуемую длительность, то нельзя быть уверенным, что ваша программа будет давать нужное время задержки при всех условиях. Длительность цикла может меняться в зависимости от применяемого компилятора (или, для Бейсика, от того, компили­ руется программа или нет). А в наше время, когда имеется большой набор машин, совместимых с IВМ РС (с широким диапазона~ скорости процессора), даже цикл на языке ассемблера может приводить к ,различным временам задержки. Поэтому разумно определять время программной задержки непосредственно по часам. Частота· отсчета 18,2 раза в секунду, используемая для модификации счетчика времени суток, должна вполне удовлетворять большинству потребностей (как увеличить частоту отсчетов см. в [2. 1.1 ]) . Чтобы обеспечить задержку данной продолжител1,ности, прог­ рамма должна подсчитать требуемое число импул1,сов счетчика времени суток. Это значение добавляется к считанному текущему значению счетчика. Затем программа постоянно считывает значе­ ние счетчика и сравнивает ero с запомненным. Когда достигается равенство, требуемая задержка прошла и можно продолжать выполнение программы. Четыре байта, в которых содержится зна­ чение счетчика времени суток, хранятся, начиная с адреса 0040:ООбС (как обычно, младший байт имеет меньший адрес). Для задержек меньше 14 с можно пользоваться. только младшим байтом. Два младших байта позволяют задержки до одного часа (точнее, на полсекунды меньше, чем час). Высокий уровень В Бейсике можно использовать оператор SOUND (2.2 .2 J со значением частоты, равным 32767. В этом случае звук не будет генерироваться вообще. Это отсутствие звука будет длиться
76 Глава 2 столько отсчетов времени суток, сколько вы укажете. Для 5- секундной задержки нужен 91 отсчет (5 х 18,2). Поэтому 100 SOUND 32767,91 •останамивает программу на 5 с Для прямого чтения счетчика времени суток нужно: 100DEFSEG=О 110 LOWBYТE = РЕЕК(&Н46С) 120 NЕХТВУТЕ = PEEK(&H46D) ·установка сегмента на начало памяти ·получение младшеrо байта ·получение следующеrо байта LОWВУТЕ ·значение двух байтов 130 LOWCOUNT = NЕХТВУТЕ*256 + Средний уровень Прочитайте значение счетчика времени суток BIOS, используя функцию О прерывания lAH, и добавьте к нему необходимое число импульсов по 1/ 18 с. После этого считывайте текущие значения счетчика времени суток, постоянно сравнивая с требуемой величиной. При достижении равенства надо заканчи­ вать задержку. Прерывание IAH возвращает два младших байта в DX (большинство задержек укладываются в этих пределах), поэтому два старших байта,- возвращаемые в •СХ, могут игнорироваться, что позволит вам избежать 32-байтовых операций. В данном примере установлена задержка на 5 с, что соответствует 91 отсчету: ;---получение значения счетчика и установка задержки MOVAH,0 INT IAH ADD DX,91 MOV BX,DX ;номер функции для чтения ;получаем значение счетчика ;добавляем 5 с к младшему слову ;запоминаем требуемое значение в ВХ ;---постоянная проверка значения счетчи·ка времени суток BIOS REPEAТ: INT IAH ;получаем значение счетчика СМР DX,BX ;сравниваем с искомым JNE REPEAT ;если не равен, то повторяем снова ;иначе - задержка окончена АТ имеет добавочную функцию прерывания lSH, которая позволяет осуществить задержку на указанное время. Поместите 86Н в АН, а число микросекунд задержки - в CX:DX. После этого 1 выполните прерывание.
Таймеры и звук 77 1.1.6 . Операцнн, запроrраммнрованные во временн Программа определя~ время для выполнения определенной операции точно так же, как и человек: берется начальное пока­ зание счетчика времени суток и затем сравнивается с последующими показан.и,ями. Можно получать значения в формате часы-минуты-секунды, но слишком хлопотно вычислять разницу между такими показаниями, поскольку система счета не деся­ тичная. Лучше прямо читать счетчик времени суток BIOS, изме­ рять продолжительность в 1/ 18 с, а затем уже переводить ее в обычный формат чч:мм:сс. l 00 GOSUB 500 110 START = TOTAL START получаем зна•1ение счетчика ·сохраняем 11ачалыюе з1111'1е1111е в . (далее идет процесс, дшпель11ость которого измеряется) 300 GOSUB 500 310 TOTAL = TOTAL - START 320 HOURS = FIXПOTAL/65520) ·получаем ф1-11шлы1ос з1шче1н1е r10дс•шп,11шем число импула,сов вы•~исляем •шсло •1асоо 330 TOTAL = TOTAL - HOURS*6552p ·выч~паем часы из TOTAL 340 MINUTES = FIX(ТOTAL/1092) вычисляем •~исло мш1ут 350 TOTAL = TOTAL - MINUTES*l092 ·вычитаем мf1нуты из TOTAL 360 SECONDS = FIXПOTAL/18.2) ·оы•шсляем число сскvнд \ • 370 PRINT HOURS,MINUTES,SECONDS ·ве•~атаем рсзут,тат 380 END 500.DEFSEG = О 510 А = РЕЕК<&Н46С) 520 А = PEEK(&H46D) 530 А = РЕЕК(&Н46Е) ·1юдщю1·рамма •пе1н1я 11ремс1аи суток ·rюлу•~аем младший байт ·получаем следующий байт и еще один 540 TOTAL = А + В*256 + С*65535 ·подсчиты11асм резуJН,тат о TOTAL. 550 RETURN осе сделано Функция TIMER в Бейсике возвращает число секунд, прошедших с момента, когда счетчи.к времени суток был пос­ ледний раз установлен в О. Обычно это число секунд, прошедших со времени последнего включения компьютера. Если при старте системы системное время было установлено правильно, то TIMER возвращает число секунд, прошедших с полуночи. Просто напишите N = ТIMER.
78 Глава 2 Средний уровень Прерывание IAH имеет две функции для установки <АН = l) и получения (АН 0) счетчика времени суток. Для чтения счетчика надо просто выполнить прерывание с АН О. При возврате значение счетчика содержится в CX:DX, причем младшее слово в СХ. AL содержит О, если счетчик не переходил через границу 24 часов с момента последней установки. Для установки счетчика поместите два слова в те же регистры, а в АН - 1. В приведенном примере измеряются промежутки времени в пределах часа. При этом нужны только два младших байта счетчик,~. Но в этом случае необходимо проверять, не было ли перехода через гра­ ницу, когда начальное значение было больше, чем следующее. ;---в сегменте данных OLDCOUNTDW О ;хра11им 1-1ачалыюе зна•1еш1е счет<тка ;---получаем начальное значение счетчf1ка 1 MOV АН,О ;номер функции INT IAH ;получаем значение счет<шк11 MOV OLDCOUNT,DX ;сохраняем началыюе значеш1е (здесь идет процесс, длитель11ость которого flЗмеряется) .-;---позднее вычисляем длительност1, процесса MOV АН,О ;1юмер функцш1 INT IAH MOV BX,OLDCOUNT СМР BX,DX JG ADJUST SUB DX,BX JMP SHORT FIG_ТIME ;---обработка переполне1шя ADJUST: MOV CX,0FFFFH ;получаем з1ш11е11ис с11етч1,1ка ~с 1 1итынаем старое з11пчс11ие ;11ро11еряем на 11ерс11от1еш1е ,обработка 11ерс11ш111еш111 ;ива•1е - берем разнооъ ;f1 11ере1юдf1м ее u обы•1111,1й шщ ·;помещаем в СХ максf1малыюе Чf1сло SUB СХ,ВХ ;вычитаем 11ер1юе з11а•1еш1е ADD CX,DX ;добавляем второе значение MOV DX,CX ;результат храним u DX ;---процедура перевода времени в обы•шый формат FIG_TIME: ;делим на 18,2 с и т.д. 2.t .7 . Управление работой в реальном времени При операциях в реальном времени программа выполняет инструкции в указанный момент времени, а не при первой воз­ можности. Такого рода операции обычно ассоциируются с робото­ техникой, но существует множество других приложений. Имеется
Таймеры и звук 79 несколько подходов к операциям в реальном времени. Для nрог­ рамм, которые не должны ничего дел~ть в промежутке между инструкциями, требующими временной привязки, можно просто периодически проверять счетчик времени суток, ожидая наступ­ ления нужного момента. Такой подход практически сводится к набору пустых циклов, описанных в [2.1.5 ]. Второй подход более сложен. Он используется, когда прог­ рамма постоянно занята какой-либо работой, но должна в опреде­ ленные моменты времени прерывать свои операции для выпол­ нения конкретной задачи. В таком случае расширяют прерывание таймера, которое выполняется 18,2 раза в секунду. Когда это пре­ рывание происходит, дополнительный код проверяет новое значе­ ние счетчика времени суток, и если наступил соответствующий момент, запускает нужную пр()цедуру. Схема процесса показана на рис. 2.3 . Приведенные здесь простые примеры демонстрируют, как создать в своей программе будильник, который устанавлива­ ется пользователем и подает звуковой сигнал, когда подошло время. (Более сложный пример низкого уровня в [2.2.61 исполняет музыку, в то время как процессор занят другими делами.) Высокий уровень Бейсик обеспечивает примитивный контроль над опе~1циями в ремьном времени посредством оператора ON ТIMER(n) GOSUB. Когда программа встречает этот оператор, она начинает отсчитывать n секунд. Тем временем выполнение программы· продолжается. Когда n секунд прошло, программа переходит на fiодпрограмму, начинающуюся с указанного номера строки, выполняет ее и возвращает управление на то место, откуда была вызвана подпрограмма. После этого отсчет снова на•1инается с нуля и подпрограмма будет вызвана повторно еще через п секунд. ON TIMER не будет функционировать до тех пор, пока он не разрешен оператором ТIMER ON. Оператор ТIMER OFF запрещает его работу. В тех случаях, когда отсчет времени необходимо продолжить, но переход на подпрограмму должен быть задержан, надо использовать оператор ТIMER STOP. В этом случае отмечается, что n секунд прошло, но переход на подпрограмму будет выполнен только после того, как встретится оператор TIMER ON. ' , Поскольку он повторяется, оператор ON TIMER особенно полезен для вывода на экран текущего времени: 100 ON ТIMER(60) GOSUB 500 110 TIMER ON ·меняем 11ока:шш111 •1асо11 кажд~ 11с-iiО ·секунд ~1 разрешаем работу таймера
80 Таблица векторов INТ 1СН (расширение 8Н) INТSH (прерывание времени суток) 8259 Контроллер прерываний 8253 Таймер IREТ INТ1CH _ _ _ _ .,__ MOVAL,~0H - OVТ20H,AL IRET Глава 2 Ваwа nроццура реалwюrо времени Проце..ура времени суток BIOS Конец Пpel)I\IUHМII времени cvroк .---- На~ало nрерывани11 времени суток Рис. 2.3 . 'Расшнреш1е 11рер1,11111ш1я таймера S00 LOCATE 1,3S:PRINT "TIME: ";LEFГ$(ТlME$,5) 'позицио11ирусм 510 RETURN ·курсор и nе•штаем время НизIQtЙ уровень BIOS содержит специальное пустое прерывание ОСН), которое ничего не делает, пока вы не напишете для него процедуру. При• старте вектор этого прерывания указывает на инструкцию IREi: (возврат из прерывания); при его вызове происходит моменталь­ ный 11озврат. Но прерывание ICH интересно тем, что оно вызыва-
.Таймеры и звук 81 ется прерыванием таймера BIOS после того, как это прерывание обновило значение счетчика времени суток .. Можно ск,1зать, что это аппаратное прерывание, происходящее автоматически 18,2 раза .:; секунду, Вы можете изменить вектор этого прерывания так, чтобы он указывал на процедуру в вашей программе. После этого ваша процедура будет вызываться 18,2 раза в секунду. О том, как написать и установить свою процедуру обработки прерывания, см. в [1.2.З ]. Написанная вами процедура должна прочита1:ь только что модифицированное значение счетчика времени суток, сравнить его с ожидаемым временем и выполнить то, что требуется, когда ожи­ даемое время, наконец, наступит. Естественно, что когда время еще не подошло, процедура просто возвращает управление, ничего не делая. Таким образом, процессор не выполняет лишней работы. В приведенном примере процедура (не показаР.ная здесь). запрашивает у пользователя число минут (до 60), кото!)ое должно пройти до того, как раздастся звонок будильника. Это L:исло, запа­ сенное в MINUTES, умножается на l 092 для перевода в эквива­ лен-:-ное число импульсов счетчика времени суток. Для периода в пределах одного часа достаточно 16 бит - более длинные периоды требуют более с.ложных 32-битовых операций. Это число импульсов добавляется к младшему слову текущего значения счет­ чика времени суток и запоминается в ALARMCOUNT. Затем вектор прерывания lCH ИЗ!.tеняется таким образом; чтобы он указывал на щ::ацедуру ALARM. Помните, что как только вектор будет изменен, ALARM будет автоматически вызы­ ваться 18,2 раза в секунду. При вызове эта процедура читает теку­ щее- значение счетчика времени суток. через прерывание !АН и сравнивает с ALARMCOUNT. П_ри совпJдении этих величин вызы­ вается процедура ВЕЕР (также не показанная здесь, см. 12.2 .4 )) , которая выдает звуковой сиrнал. В противном случае происходит возврат. Обычный код возв·рата из аппаратных прерываний (MOV АН,20Н / OUT 20H,AL) включать в процедуру не нужно, так как он будет в прерывании таймера. Будьте внимательны и не забудьте сохранить изменяемые регистры. ;---в сегменте данных MINUTES DW О ALCOUNT DW О ;хра~1ит ч~1сло мш1ут до звонка ;хранит сче~:•шк времеш1 для звонка ;---установка ожидаемого значения счетчика времени суток CALL REQ_MIN MOV AX,MINUTES MOV ВХ,1092 MUL ВХ ;запрос •1ис;~а м~111ут до зво11ка ;перес1,1дка в АХ ;число 11мпудьсо11 с•1етчика в ми11уте ;умножаем - резут,тат в АХ
82 Глава 2 ;получаем текущее значение счетчика MOV АН,О ;номер функции чтения счетчика INT IАН ;читаем значение, младший байт в DX ;складываем оба значения ADD AX,DX MOV ALCOUNT,AX ;---заменяем вектор пустого прерывания PUSH DS моv AX,SEG ALARM моv DS,AX моv I)X,OFFSET ALARM моv AL,ICH моv АН,25Н INT 21Н РОР DS ;.---дальше продолжается программа ;получаем нужное значение счетчика ;сохраняем сегмент данных ;берем сегмент процедуры ALARM ;помещаем его в DS ;берем смещение процедуры ;номер изменяемого вектора ;функция изменения вектора ;меняем вектор ;восстанавливаем сегмент данных ;---в конце программы возвращаем вектор п~рывания MOV DX,0FFSЗH ;оригинальные значения для MOV AX,0FOOOH ;прерывания 1СИ MOV DS,AX MOV AL,ICH MOV АН,25Н INT 21Н ;---процедура выдачи звукового сигнала ;помещаем сегмент в DS ;номер изменяемого вектора ;номер функции ;восстанавливаем вектор ALARM PROC FAR ;создаем далекую процедуру PUSH АХ PUSH СХ PUSH DX ;---читаем счетчик времени суток моv ли.о INT !АН . ;--- сравн ивае м с требуемым значением ;сохраняем изменяемые регистры ;номер функции чтения счет•1ика ;читаем значение счетчика MOV CX,ALCOUNT ;берем требуемое значение· СМР DX,CX ;сравниваем с текущим JNE NOT_УЕТ ;если не равны, то на выход ;---выдаем звуковой сигнал, если значения совпали CALL ВЕЕР ;эта процедура не показана
Таймеры и звук 83 ;---иначе - возвращаемся из прерывания NOT_YET: РОР DX ;воссташ111ли11асм рсI·истрI,I ALARM РОР СХ РОР АХ IRET ENDP ;возпрат ~,з 11рерI,111аш1я ;KOIICI\ 11pшic;iypI,I 1.1.8 . Генерация случайных чисел с помощью микросхемь1 таймера Для генерации последовательности случайных чисел требуются сложные математические манипуляции. Но иногда программе в определенный мом~нт требуется только одно случайное число, которое может быть получено просто чтением из канала микро­ схемы таймера. Бейсик использует это число в качестве ядра, по которому генерируется случайная последовательность. Конечно, вы не можете использовать ряд последовательно считанных значений в качестве случайной последовательности, так как сами по себе интервалы времени между считываниями будут неслучайными. Высокий уровень • Бейсик имеет генератор случайных чисел, который может быть перезапущен с помощью оператора TIMER, с тем чтобы при каж­ дом запуске программы генерировалась другая цоследовательность слун:айных чисел. Напишите RANDOMIZE ТIMER, а затем используйте функцию RND для получения случайных чисел. 100 RANDOMIZE TIMER ·сброс I-е11ератора случайных чисеJI 110 PRINT RND,RND .RND ·r1ечап, тrсх слу 1 ~ай111,1х •I~1сел в результате получаем: 0,7122483 0,4695052 0,9 32487 Низкий уровень Поскольку регистр счетчика канала таймера перезагружается снова и снова данным числом (а в промежутках идет счет вниз до 0), выберите в качестве загружаемого в счетчик значения число, равное требуемому диапазону случайных чисел. Например, для получения случайного значения часа дня загружайте в счетчик 23. Лучше всего использовать режим 3 канала 2 (порт 42Н)· микросхемы таймера [2.1 .1 ]. Сначала установите для счетчика желаемый диапазон случайных чисел (в примере используется 10000, что приводит к выдаче случайного чнсла в· диапазоне от О
84 Глава 2 ! до 9999). Затем, чтобы получить из канала случайное число, надо подать команду командному ·регистру микросхемы таймера через порт 43Н перенести текущее значение счетчика в регистр "задвижки", для чего надо сбросить биты 4 и 5. Этот перенос в регистр задвижки не мешает продолжающемуся счету. Затем уста­ новите оба бита 4 и 5 командного регистра, чтобы процессор мог читать из регистра задвижки. После этого две инструкции IN дадут сначала младший, а затем старший байт в регистре AL. Наконец, восстановите первоначальное значение регистра задвижки, чтобы счет продолжался в пределах указанного диапа­ зона времени. ;---установка адресов портов COM_REG EQU 43Н CНANNEL_2 EQU 42Н CALL SET COUNT ;адрес командного регистра ;адрес канала 2 ;установка диапазона ;---здесь программа работает, а затем требует случайное число CALL GET NUM8ER ;---начинаем отсчет канала 2 SET_COUNT PROC MOV AL,I0II0II08 оuт COM_REG,AL MOV АХ,10000 OUT CHANNEL_2,AL MOV AL,AH OUT CHANNEL_2,AL RET SET_COUNT ENDP ;---получение случайного ч~1сла READ_NUM PROC ;получение случайного •1~1сла ;канал 2, реж~1м 2, оба байта ;посылаем о команд111,1й регистр ;зна,1ен:1,1е с11ет1.1ика ;посылаем младший байт ;передв~1гаем старший байт II ЛL ;посылаем старший байт ;---пересылаем значение счетчика в регистр задuf1жкf1 MOV AL,100001I08 ;требуемая команда OUT COM_REG,AL ;посылаем 11 комаид111,1й реп,стр ;---читаем значение счетчика MOV . AL,I0II0II08 OUT COM_REG,AL IN AL,CНANNEL_2 MOV AH,AL IN AL,CНANNEL_2 CALL SET_COUNT SWAP AH,AL ;з."lпрос на чте1те/заш1сь ;посылаем запрос ;полу•шем младiш1й байт ;uремешю хра1~им его II АН .;полу•шем старший байт ;восстанаuлш1аем З."\дnижку • ;ставим байты 1ш место
Таймеры. и зву,с RET READ_NUM ENDP 85 ;теперь слу11айное 11нсло в АХ 2.2 . Соэдание звука Бейсик оснащен достаточно изощренными средства~ для гене~;щии звука, однако операционная система позволяет только просто подать звуковой сигнал. Если вы хотите получить какие-либо сложные звуки, то вы должны программиро-. вать микросхему таймера 8253. Канал 2 этой микросхемы прямо связан с динамиком компьютера. Когда этот канал программиру­ ется в режиме 3, он посылает прямоугольные волны данной частоты. Из-за простоты динамика он сглаживает края прямо­ угольной волны, получая более приятную для слуха синусо­ идальную вОJiну. К сожалению, микросхема 8253 не меняет амплитуду вОJiны, поэтому мы не можем менять громкость звука, издаваемоrо динамиком. Динамик имеет не один, а два входа для генерации звука. На рис. 2.2 в (2.1 .1] показано, что, кроме микросхемы таймера, сигнал посылает также микросхема интерфейса с периферией 8255 (1.1 .1 ]. Частота импульсов каждой микросхемы может быть изме­ нена, поэтому, комбинируя воздействия этих двух источников, мы можем пмучать специальные звуковые эффекты. -Тмько PCjr имеет специальную микросхему, управляющую генератором звука. Он может одновременно выдавать три разных тона плюс шум для звуковых эффектов. Громкость каждого из трех каналов может устанавливаться независимо. Другой уникаль­ ной возможностью PCjr является то, что он может управлять внешним источником звука, таким, как кассетный магнитофон. 2.2 .1 . Проrраммнрование rенератора звука 76496 (только PCjr) PCjr снабжен 4-канальным генератором ,звука, в котором три канала генерируют тона, а четвертый служит для генерации шума для звуковых эффектов. Все четыре канала программируются независимо, причем каждый имеет ,свой регулятор громкости, а затем выход с них объединяется в единый звvковой сигнал. Используется микросхема комплексного генерат"ора • звука TI SN76496N. Она имеет 8 регистров, по 2 для каждого канала, и все они адресуются через один порт с адресом ОСОН. Этот порт
86 Глава 2 • служит только для записи; если подать инструкцию IN, то вся сис­ тема будет заморожена. PCjr имеет также разъем для внешнего источника звука. При старте системы звуковой канал получает выходной сигнал от микросхемы таймера 8253. Но этот канал может быть переключен на микросхему генератора звука или любой из двух внешних зву­ ковых входов. Это достигается изменением битов 5 и 6 порта В микросхемы интерфейса с периферией 8255 (адрес порта 61 Н, см. [1.1.1 ]) . Значение битов следующее: Биты6и5 00 01 10 11 Выбранная фvвкция микросхема таймера 8253 uход с кассетного маr1нпофо11а нход канала нвода/nыnода генератор звука 76496 Для выбора источника звука в BIOS PCjr добавлена функция 80Н прерывания lAH. Поместите в AL номер кода от О до 3 в соответствии с приведенной выше таблицей, и вызовите функцию. Возвращаемых регистров нет. Генератор звука 76496 должен использовать этот звуковой канал, поскольку он нс может управ­ лять внутренним динамиком PCjr. В общем случае, когда байт данных посылается генератору звука, биты 4-6 содержат код идентификации, сообщающий, какому из восьми регистров предназначены данные. Эти коды такие: Биты 6-4 ООО 001 010 011 100 101 110 111 ЛдрСС\'СМЫЙ рсI·~~стр частота rIерIюIх, тона rромкосп, 11ер11ою тоI1а •~астота второго тона громкост1, нтороIхJ тона частота трст1)е1·0 н,11а 1·ромкостI, треп,еrо тош1 частота четве,}то1·0 то1 ia I·ромкосп, чсп1ертою тош1 В случае регистров частоты тонов требуются два байта. Значение битов при этом следующее: байт 1: биты 0-3 младшие 4 бита частоты 4-6 код иде1пиф~1кацш1 регистра
Таймеры и звук байт 2: биты 7 0-5 6 7 всегда равен l старшие 6 бит частоты не используется всегда равен О 87 Для установки частоты тона в регистр посылается 1О-битовое значение, которое после деления на 111843 дает желаемую частоту в герцах. Таким образом, доступны частоты, начиная со 11 О Гц и больше. 011843 / 2· 1О). Как только регистр инициализирован (и соответственно установлен порт В микросхемы 8255), немедленно начинается звуковой сигнал и продолжается до тех пор, пока он не будет прекращен. Не обязательно для изменения частоты посылать новые два байта. Если послан только второй байт (старшие 6 бит частоты) , то он автоматически заменяет соотве:гствующие данные в канале, к которому была последняя адресация. Эта возможность позволяет плавно варьировать частоту. Генератору :щума для программирования нужен только один байт. Значенщ: битов для· него следующее: биты 0-l 2 3 4-6 7 плотность шума качество шума не используется код идентификации регистр.'1 всегда установлен в l Качество шума устанавливается на белый шум (постоянное шипени~), когда бит 2 равен l, и на периодический шум (волны звука), когда бит 2 равен О. Плотность звука увеличивается при увеличении битов 0-1 от 00В до lOB; когда они установлены в 11В, звук меняется в зависимости от ·выходного тона канала З. Громкость каждого из четырех каналов изменяется в резуль­ тате ослабления основного сигнала. Для эт9'й установки требуется только один байт. Значение ero битов. следующее: биты 0-3 4-6 7 ослабление сигнала код идентификации регистра всегда установлен в l Когда все 4 бита данных равны О, громкость максимальна. Когда все они равны l, звук полностью подавляется. Для полу­ чения звука промежуточной громкости может быть использована любая комбинация битов. Бит О ослабляет звук на 2 Дб (деци­ бела),бит1на4Дб,бит2-на8ДбибитЗ-на16Дб.Макси­ мальное ослабление равно 28 Дб.
88 Глава 2 1.1.1. Генерация тона Этот параграф объясняет, как производить звук, когда ком­ пьютер не занят ничем другим; в [2.2 .3] показано, как это сде­ лать, когда производятся другие действия. Забавно, но для прог­ раммистов на ассемблере последнее проще. Для этого достаточно запрограммировать микросхему таймера 8253, которая работает независимо от процессора. В приведенном здесь методе процессор непосредственно управляет динамиком, поэтому программе при­ ходится выполнять работу, которую может выполнять микросхема таймера. Хотя этот способ более труден, он допускает существенно больший контроль за динамиком, и создание большинства спе­ циальных звуковых эффектов (см. [2.2 .8 ]) основывается на нем. Высокий уровень Оператор Бейсика SOUND используется для генерации тона в широком диапазоне частот и длительностей. Частота дается в герцах (от 37 до 32767), а длительность в импул1,сах счетчика времени суток BIOS (от О до 65535), причем в секунду происходит 18,2 импульса. SOUND 440,91 воспроизводит ноту А в течение 5 с (5 х 18,2). Частоты первой октавы, начиная с ноты С(до), таковы: С(до) 523.3 D(pe) ,587 .3 • Е(ми) 659,3 F(фа) 698,5 G(соль) 784,0 А(ля) 880,0 В(си) 987,7 Частоты на октаву выше можно получить, удваивая эти значе­ ния, на две октавы выше - еще раз удваивая частоты. И наоборот, частоты на октаву ниже равны приблизительно половине этих зна­ чений (хорошо настроенное пианино точно не следует арифмети- ческим интервалам). • Благодаря своему генератору звука [2.2.1] PCjr может исполь­ зовать оператор SOUND для трех независимых каналов звука, причем громкость каждого из них управляема. В этом· случае формат оператора: SOUND частота, длительность, громкость, канал. Громкость может меняться от О до 15, по умолчанию - 8 . Номер канала может меняться от О до 2, по умолчанию - О.
Таймеры и звук 89 Поскольку PCjr может использовать возможности мноrоrолосия и контроля звука только для внешнеrо динамика, надо сначала разрешить этот динамик. Это делается с помощью оператора SOUND ON. SOUND OFF передает контроль внутреннему динамику. Чтобы сыграть аккорд D-минор (ре-минор) (D-F -A) с малой громкостью, напишите: 100 SOUND ON 110 SOUND 587,50,3,0 120 SOUND 699,50,3,1 130 SOUND 880,50,3,l . Низкий уровень разрешение внешнего динамика ·нота ре ·нота фа ·нота ля Генерация звука с помощью адаптера интерфейса с перифе­ рией 8255 состоит во включении и выключении с желаемой часто­ той бита порта В, который связан с динамиком (бит 1). Порт В имеет адрес 61Н (хотя АТ не имеет микросхемы интерфейса с периферией 8255 как таковой, он использует для этой цели тот же адрес порта и тот же бит). Если программа переключает значен~е бита с максимально возможной частотой, то частота слишком высокая, чтобы быть полезной. Поэтому между двумя переключе­ ниями надо вставлять пустой цикл. Помните, что бит О порта В управляет воротами канала 2 микросхемы таймера, который в свою очередь связан с динамиком. Поэтqму этот бит сбрасывается, отсо.единяясь от канала таймера. На рис. 2.4 показано, как этот метод позволяет установить частоту звука. В следующем примере введены две переменные. Одна, обоз­ наченная "FREQUENCY", используется в качестве счетчика в пустом цикле между действиями включения и выключения. Чем меньше ее значение, тем быстрее происходит изменение бита и тем больше частота. Переменная же "NUMBER CYCLES" уста­ навливает продолжительность тона. Она rоворит, сколько раз должен быть повторен процесс включения и выключения. Чем больше это число, тем дольше звучит данный звук. Отметим, что для этой процедуры аппаратные прерывания должны быть запрещены. Причина этого в том, что прерывание таймера происходит с такой частотой и регулярностью (18,2 раза в секунду), что оно будет существенно влиять на частоту. Имейте в виду, что пока прерывания запрещеt~ы, счетчик времени суток BIOS не будет работать. Если затем прочитать ero значение, то - оно будет отличаться на некоторую величину от реального до тех пор, пока не будет сделано соответствующее изменение.
90 Глава 2 Вкnючен 1 L j111 С11гнаn ~ыкnючен • + + С1 1@1 i@ С1 300 ., s s i цмкn iцмкns :i:: цмкnов · :,: :i:: :i:: С1 f i зцержк11 С1 Q зцержк11 С1 ., Q ., 2 2 -~ 2 с:; ~ с:; с:; "' "' ! "' :i5 • • • • • + + Счетчикдn11теnьност11:отсчет .............. . отсчет .............. отсчет Теперь укорачиваем ц11кnы эадерж1<м дn11 увеnмчен1111 частоты NUM_CES FREQ PORT_B 150 цмкnов ссссс Рис. 2.4 . Генерац~iя звука м~1кросхемоi'1 8255 EQU 1000 EQU 300 EQU 61Н CLI ;запрет прерываний MOV DX,NUM_CES ;длительность тона 11 DX IN AL,PORT_B ;полу•шем з11а•1еш1с нз ш,р·(а 8 AND AL, 111111108 ;отклю•шем дюшмик от таймера NEXT CLE: OR AL,000000108 ;включаем динамик OUT PORT_B,AL ;посылаем команду 11 ,юрт Н MOV CX,FREQ ;з.'1дсржка ш1 1юлцик;ш 11 СХ FIR_HALF: LOOP FIR_HALF ;делаем з;щержку AND AL,111111018 ;выключаем д~шамик OUT PORT_B,AL ;посылаем команду в порт В MOV CX,FREQ ;задержка на ш,nцикла о СХ
Таймеры и звук SEC_ HALF: LOOP SEC_ HALf' DECDX· JNZ NEXT CLE SТI ;делаем задержку ;uыч1-паем сди111,1цу 1-1з с 1 Iс·р11-1ка ;есл1,1 О. то надо зака11 1 1ивап. 91 1.1.3 . Генерация звука одновременно с другнмн действнямн Для программистов на Бейсике различие между этим и преды­ дущим параграфом несущественно. Но проrраммисты на ассем­ блере должны использовать совершенно иной. метод. Поскольку микросхема таймера 8253 работает независимо от процессора, очень легко генерировать звук, который издается одновременно с_, выполнением других операций. Вы должны просто запрог­ раммировать •канал 2 этой микросхемы для rенерации опреде­ ленной часто.ты, а затем перепроrраммировать микросхему для выключения -звука. ' Высокий уровень Оператор SOUND в Бейсике не позволяет rенсрировап, звук одновременно с другими действиями, но оператор PLA У, если ему это задать, позволяет. За оператором PLAY должна слсдовап, строка, которая сообщает, какие ноты должны быть сыrраны, какой длительности, а также другие характеристики. Детали командной строки PLAУ обсуждаются в [2.2 .5 ]. Если строка содер­ жит буквы МВ (фоновая музыка), •то она. помещается в специ­ альный буфер и выполняется одновременно с друrими програм­ мными действиями. Напротив, MF (музыка на переднем плане) останавливает все программные операции до тех пор,. пока не будет исполнена вся строка. Вот как воспроизвести исполнение одной ноты А (ля) в фоновом режиме: 100 PLAY "МВ А" 110 ...... нс110лняетси нота ли ... н слсдующ~1е онераторы про1·раммы Отметим, что в фоновом режиме оператор Х PLA У (0) возвращает число нот (до 32), которое осталось сыrрать. • В многоканальном режиме на PCjr возвращается число нот в буфере данного канала (0-2), номер которого указан в скобках. Низкий уровень Пошлите счетчик в канал 2, как объяснено в 12.1.1 J. М икро­ схема должна быть предварительно разрешена через порт В микро-
92 Глава 2 схемы интерфейса с периферией 8255 (адрес бlН>. Вычислите тре­ буемое значение счетчика для задвижки, разделив 1, 19 миллиона на требуемую частоту в герцах. Звук будет продолжаться до тех пор, пока не будут закрыты ворота канала 2. Поэтому вы должны сбросить бит 1 порта В в О, иначе звук будет продолжаться беско­ нечно и -может быть прекращен только при перезагрузке ком­ пьютера. Для точноrо регулирования длительности звука можно использовать счетчик времени суток BIOS (см. [2.1 .6 ]) . В данном примере генерируется частота 440 Гц. Звук прекращается после нажатия любой клавиши на клавиатуре. ;---разрешение канала 2 установкой порта В микросхемы 8255 PORT В EQU61Н ;установка адреса порта В IN AL,PORT_B ;чтение его значения OR AL,3 ;установка двух младших битов _OUT PORT_B,AL ;посылаем байт в порт В ;---установка регистров ввода/вывода COM_REGEQU 43Н ;адрес кома,щ,юго регистра CHAN 2 EQU 42Н ;адрес канала 2 MOVAL,101101108 ;цепочка битов для ка~~ала 2 OUTCOM_REG,AL ;засылка в кома~щ11ый регистр ;---засылка счетчика в задвижку MOV АХ,2705 ;с•1ет•1ик = 1190000/440 OUT CHAN _ 2,AL ;пос1,1лаем млад~ш1й бМ11· MQVAL,AH ;сд1шrаем младший б.1(1т II AI, OUTCHAN_2,AI, ;1юсылаем старший байт ;.---ждем нажатия клавиш~, MOVAH,l INT 21Н ;---выключение звука ;номер фу11кции 11реры11:1ш1я 2111 ;вызываем прерывание IN AL,PORT_В ;получаем байт из порта В AND AL, 111111008 ;сбрасываем два младших бита OUT PORT_B,AL ;посылаем байт обратно 1.1 .4 . Генерация однотонноrо сиrнаnа Некоторым программам требуется набор предостерегающих гудков. Их легко создавать на Бейсике, но операционная система ·не обеспечивает функцию гудка как таковую и только косвенно позволяет получать доступ к гудку, который вы слышите при старте системы. Для изменения тона вся процедура генерации
Таймеры. и звук 93 звука должна быть запрограммирована на низком уровне. Для того чтобы .гудок соответствовал подаваемому им сигналу, необходимо проявить воображение. Для предсказания близкой опасности соз­ дайте набор понижающихся тонов (2.2 .7] или, если принтер вклю­ чен, чередуйте гудки динамика компьютера и принтера (вывод кода ASCII 7 на принтер). Высокий уровень ... В Бейсике просто напишите ВЕЕР. Вот кусочек кода, который реагирует на вероятную ошибку гудком и запросом: 1()0 INPUT "Enter your age" ,AGE ·запрос Iюзраста 11 О IF AGE > 100 THEN BEEP:PRINT"Аге y0t1 rcally оvег 100'!'' Для гудков другой частоты и продолжительности используйте оператор SOUND. Его форма: SOUND частота, длительность , где частота представлена в герцах (3000 - середина диапазона), а дли­ тет,ность - в восемнадцатых долях секvАды. SOUND 3000, 18 дает гудок длительностью около одной секунды. В приведенном примере динамик быстро переходит от пысокого тона к низкому и обратно, "распугивая" все живое п б:,11жайшей окрестности. • 100FORN=1ТО200 110 SOUND 500,1 120 SOUND 5000,1 130 NEXT Средний уровень 'ус·11111овка t1исла 1101порс11нf1 ·з11ук 1,н1зкоii •шстоп~ ,ш I сскуII,•1у ·звук ш,1соко~i 1 шстот1,1 на I ccкy11Jiy ·,ю,пор Операционная система не предосtавл-яет специальной функции для генерации звука. Но вы можете пызпать знакомый гудок, просто подавая код ASCII 7 на стандартное устройство пыпощt (терминал), используя одну из функций DOS или BIOS. Код ASCII 7 интерпретируется как управляющиft симпол "зпонок" и нс рисуется на экране. Проще всего применять функцию 2 преры­ вания 21Н: MOV АН,2 MOV DL,7 INT 21Н ;функцш1 nыJl(JЩI пIмIю.:ш ,ш :жр:1II ;nос1,uшем код ЛSCII 7 ;динамик t')'д~п
94 Глава 2 Низкий уровень Для простого гудка лу~ше всего подходит метод, основанный на использовании микросхемы интерфейса с периферией 8255 (см. [1.1.1 ]). Ниже приведен пример, практически повторs~ющий гудок, который вы: слышите при старте системы. ;---гудок динамика MOV DX,800' IN AL,61H AND AL,0FEH NEXTCYCLE: OR AL,2 оuт 61H,AL MOV СХ,150 CYCLEUP: LOOPCYCШUP ' AND AL,0FDl-1 OUT 611-1,ЛL, ;счет•1ик ч~•сла ц~tкдов ;читаем порт В 8255 ;выКJ1ю•~аем бит таr1мсра 8253 ;включаем бит ди1шмика ;посылаем баrп 11 порт n ;ДJштелы1ость nср1юй nшю1шны ;задержка, шжа сш·11аJ1 111,1сок~1й ;111,1кJ1ю•шем б~п д~1ш1м~1ка ;носылаем байт 11 1ю1п В CYCLEDOWN: LOOP CYCLEDOWN ;задержка, 1юка c~ll'IШл низкий DEC DX ;уме111,1ш1ем с•1сршк цr1кJю11 JNZ NEXTCYCLE ;1ю1пор11ем цf1к;1, 1юка DX 11е О 1.1 .5 . Генера~я набора тонов В этом параграфе показано, как генерировать цспо•1ку звуков, когда компьютер ничем другим не занят; а в следующем мы рас­ смотрим, как выполнить ту же задачу, если комп1,ютср занят дру­ гой работой. Когда компьютер ничем другим нс занят, можно выводить мелодию или производить спсциал~ныс звуковые эффекты; когда же компьютер занят другой работой, произво,1ить звуковые эффекты нельзя. • Создание звуковых строк - одна из мощнейших возможностей, предоставляемых Бейсиком. Построение же строк звуков в ассем­ блере требует большой работы. Может быть использован любой из двух методов генерации звука, предложенных в [2.2 .2] и [2.2 .3 ]. Для обоих методов надо просто генерировать один тон в течении заданного времени, затем следующий и т.д. Каждая звуковая строка формируется из двух строк данных, одна из которых содер­ жит частоты: последовательных тонов, а другая хранит их длитсл~,­ ности (при условии, что требуются разные длительности). Продол­ жительность звучания определяется с помощ1,ю счст•шка времени суток BIOS (2.1 .6 ].
Таймеры и звук 95 высокий уровень Оператор Бейсика PLAУ предоставляет бол1,шие возможности. Оператор сопровождается строкой нот, перемешанных с инфор­ мацией о том, как эти ноты должны быть исполнены. Ноты запи­ сываются буквами А - G с последующими знаками для диезов и бемолей. Диезы обозначаются знаками # или +, а бемоли - минvсом (-). Операторы PLAY "CC#D" и PLA.Y "CD-D" экви­ валентны, но нельзя использовать знаки диезов и бемолей для обозначения звуков, соответствующих белым клавишам. Второй способ задания нот состоит в вычислении кодового номера от О до 84, причем О соответствует отсутствию звучания, а числа от I до 84 соответствуют 84 возможным нотам семи октав, начиная снизу. Номеру должна предшествовать буква N: PLAY "NЗN72N44". Допустимый диапазон - семь октав, внутри каждой могут быть ноты от С(до) до В(си). Октавы пронумеров,tны от О до 6, и нота до первой октавы соответствует октаве 3. Текущая октава может быть изменена в любой момент за счет вставки в строку буквы О, за которой следует номер октавы. Если нс было начальной установки, то используется октава 4. Оператор PLAY "O3СО4СО5СО6С" выводит ноты С(до) последовательных октав вверх. Другой способ изменения октавы состоит во включении в строку символов > или <, которые переключают тон вверх и вниз на октаву соответственно. Оператор PLA У "O3С>С>С>С" приводит к тому же резу,1ьтату, что и предыдущий. Длительность нот также может быть изменена за счет вставки кодового номера, которому предшествует буква L. Все после­ дующие ноты будут исполняться с ::>той длитс,11,носп,ю до тех пор, пока не в.:трститсs; друrои код длины. Код - ;:,то число от I до 64, причем l сс,ответствует це.1ой ноте, а 64 - 1/64. Затю, L4 соот­ вегствует чt:тверти. Темп исполнения нот регуJ1ируется кодом темпа, состоящим из буквы Т, за котороii следует число от 32 до 255, дающее количество четвертей, исполнs1ем1>1х в минуту. Если эти параметры не указаны, то по умолчанию берется длител1,носп, L4 и темп 120. Для изменения длител1,ности только одной ноты надо поместить значение длиш>1 после ноты 11 без буквы L. Опера­ тор PLAУ "L4CDE16FG" исполнит Е как шестнадцатvю, а вес остальные ноты - как четверти. Д,1итет,ност1, пауз берется такой же, как и длительность нот. Поместите· число от I до 64 после буквы Р для паузы. Pl делает паvзv длитет,носп,ю в целvю, а Р64 - в l /64. Помещение точки пос.,;е ноты 11меет тот же с~ысл, что и в обычной музыкальной нотац~1и: длите,11,ност1, ноты увели­ чивается наполовину. Вторая точка продолжит д-,11пс,11,носп, еще наполовину.
96 Гл.tва 2 По умолчанию ноты имеют длительность 7 /8. Чтобы они зву­ чали с полной длительностью (легато), поместите в строку ML. Если необходимо звучание с длительностью 3/ 4 (стаккато), поместите в строку MS. Чтобы вернуться к нормальному стилю, надо указать MN. Обычно вся прочая деятельность прогр.1ммы прекращается до тех пор, пока не будет сыграна строка. Для того чтобы выпол­ нялись операторы, следующие за оператором PLA У, а строка исполня..,ась в фоновом режю.1е, поместите в строку МВ. Для вос- становления нормальной ситуац~и напишите MF. ' Наконец, оператор PLAУ позволяет исполнять подстроки внутри длинной- строки. Имеется в виду, что часть исполняемой строки может быть введена как обычная строков_ая переменная, а затем эта переменная может быть вызвана из строки, сформи­ рованной в операторе PLAY. Например, если S$ = "ЕЕЕЕЕ", то в операторе PLAУ "CDXS$;FG" нота Е будет повторена 5 раз. Отме-' тим, что имени переменной должна предшествовать буква Х, а за именем следовать точка с запятой (;). (Для компилируемых программ применяется другой метод с использованием переменной VARPTR$, детали см. в руководстве по Бе.йсику.) В приведенном примере исполняется знакомый бой "дедуш­ киных" часов. В строке сначала устанавливается стил1, исполнения легато, затем темп и начальная октава, и, наконец, четыре ноты, пауза, и те же четыре ноты, но в обратном порядке. Пробелы в строке включены только для удобства программиста - Бейсик игнорирует их. 100 PLAY "ML Т40 03 ECD<G РЗ2 G>DEC" Благодаря наличию ~енератора звука PCjr добавляет к опера­ тору PLAУ две возможности. Во-первых, допускается параметр V, устанавливающий громкость. Выражени€ VS устанавливает (или изменяет) громкость на уровень 5. Допустимый диапазон от О до 15, причем по умолчанию берется 8. О полностью подавляет звук. Во-вторых, с помощью оператора PLA У можно одновременно исполнять три звуковые строки. Поместите все три строки в одну программную строку, разделяя их запятыми. Для того чтобы иметь возможность использовать эти специал1,ные свойства, вы должны предварительно разрешить внешний динамик с помощью оператора SOUND ON. 100 SOUND ON 11О PLAУ "........... "," .......... "," ............ " r<
Таймеры и звук 97 Низкий уровень В примере для генерации звука исполr,зуется микросхема тай­ мера 8253. Здесь просто исполняются 8 нот, но нсбоm,шая модифи­ кация может сильно расширить возможности этой процедуры. Имеются три строки данных. Первая устанавливает длительность каждой ноты как кратное произвольного периода задержки (изме­ няя этот период задержки, можно изменять темп). Вторая строка содержит ,частоты каждой из 8 нот; эти значения должны быть помещены в регистр задвижки канала 2 микросхемы 8253 для исполнения желаемых тонов. Третья строка .содержит мелодию в виде кодовых номеров от l до 8; которые соответствуют восьми частотам. Эта строка завершается кодом OFFH, который служит признаком конца мелодии. Процедура просто читает очередную ноту мелодии, находит соответствующую частоту и помещает се в канал 2. Затем длительность для этой ноты помещается в счетчик цикла задержки, который использует счетчик времени суток. Когда задержка кончается, переходим к обработке следующей ноты. На рис. 2.5 показан.:i работа .этой процедуры. ;---в сегменте данных DB 10,9 ,8,7 ,6 ,5 ,4 ,3,2 ВЕАТ FREQ DW 2280,2031, 1809, 170~ • • D W 1521,1353,1207,1139 MELODY DB 1,2 ,3 ,4 ,5,6 ,7 ,8 ,0FFH ;w1итe;,t,IIOCIЪ 110'1 ' ;таб,;нн~а частот ;номер 1 1астоты ноты ;---инициализация PORT_B EQUбlН СОМ_REG EQU 43Н ,LATCH2 EQU 42Н JN AL,PORT_B OR AL,00000011В О UT PORT_B,AL MOVSl,0 MOVAL,0B6H ;11олу11асм теку11~иi"'1 статус ;разрешаем дюIамнк н таliмер ;замеш1ем байт ;ншщиалвз~1русм указатсл1, ;устано11ка для канала 2 OUTCOM_REG,AL ;посы;1аем в !(Ома1щ11ый рсI·нстр ;---смотрим ноту, получаем ее чnстоту и помещаем n канал 2 NEXT: LEA BX,MELODY ;берем смещеш1е для мелrщш, 4 .Р. джордейн MOVAL, [ВХ] [SI) ;берем код п-й 1юп,I строк~~ CMPAL,OFFH JE NO_MORE cnw ;11ро11срка на ко1Iец строки ;если конен. то на выход ;персIюд~,м IJ CJIOIIO \
98 Глава 2 2 34 5678 Строка частот Получаем следующую ноту Берем частоту ДЛII ЭТОЙ НОТЫ Помещаем в таймер 8253 8253 таймер Строка мелодии 56654433 \\ Получаем число тактов, соответствующее данной ноте Добавл11ем к счет- Область данных BIOS чику времени суток Провер11ем зна­ чение счетчика до тех пор, пока он не станет равным сум­ ме Рис. 2.5 . ИCIIOJJIICШIC с1·рок,1 IIOT ;получение частоты MOVBX,OFFSET FREQ ;смещсш1е табшщы частот DEC АХ ;на•шнаем отсчет с О SHL АХ,! ;умножаем на 2, так как с;ю111шs1 табшща MOVDl,AX ;адресуем через 1)1 MOVDX,(BX] [DI) ;получаем частоту нз п1бшщ1,1 ;начинаем исполнение ноты MOVAL,DL OUT LATCH2,AL MOVAL,DH OUT LATCH2,AL ;---создание цикла задержки MOVAH,0 INT !АН ;1'0товим м;шд1111-1й байт trаснпм ;11ос1)1ласм с1·0 ;1·отов1-1м стар111иr1 бай·1 11ас1·отt»1 ;IIосыласм с1'<) ;номер функции •пеш111 с•1етчнка ;получаем зна 11е1Iис c1.1t:1 tI1rIкa MOVBX,OFFSET ВЕАТ ;смещение таблицы длин MOVCL,[BX] [SI) ;берем длш1у очередной ноты моvсн,о MOVBX,DX ADDBX,CX ST SOU: INT IАН ;берем младшее с;ю1ю счстч,1ка ;онрсделs~см MOMCIIТ OKOIL'laHИSI ;берем Зlla'ICIIИC сче•1·1.1_и~а
Таймеры и звук CMI' DX,BX JNE ST_SOU INC SI JMP NEXT ;---з::шерщеш1е NO_MOR: IN AL,PORT_B ANDAL,0FCH OUT61H,AL ;срашн-шасм с 0~011ча11исм ;11сраш,ы - 11ро1tоJ1жасм звук ;11ерехоним ,к сленующей 1юл.: ;получаем статус ,юрта В ;выкл1очаем ди1шмик ;заменяем байт 1.1.6 . Генерацня строкн тонов одновременно с друrнмн опl!рацнямн 99 Хотя в Бейсике это делается очень просто, на. самом деле подобная. задача представляет собой нетривиал1,ный трюк програм­ мирования в реальном времени. Для решения такой задачи нужна генерация звука через микросхему 8253 [2.2.3 J, так как метод, использующий микросхему 8255 [2.2 .2 ], занимает процессор. Соответственно только строки чистых музыкальных тонов могут производиться таким методом - всякого рода звуковые :>ффскты при этом •, недоступны. Основная техника программирования в реальном времени показана в [2.1 .7]. Программы, работающие в реальном времени, модифицируют прерывание таймера, которое останавливает процессор 18,2 раза в секунду, чтобы изменить показание счетчика времени суток. Расширение процедуры преры­ вания сравнивает новое значение счетчика ·времени суток со зна­ чением, показывающим время завершения rенер.tции тона, и когда это значение достигнуто, прерывает звук, начинает генерацию другого тона и устанавливает время его окончания. Высокий уровень Генерация строки звуков одновременно с другими операциями является одной из возможностей очень мощного оператора PLAУ, который детально обсуждался в [2.2,5 1. Надо просто добавит~, в начало управляющей строки МВ. Это сокращение от Music Background (фоновая музыка); для того чтобы заставить PLAУ прекратить все другие операции, пока генерация звуковой строки не будет завершена, вставьте MF. В приведенном ниже примере во время рисования и заполнения рамки исполняется гамма (для его демонстрации требуется наличие графических возможностей). 100 PLAY "МВ ТIОО 03 L4;CDEFG>ABC" ·~1спол11яем набор ,ют 110 LINE O0,10)-(80,80),1 ,BF ·од11оорсме111ю рисуем рамку 4*
100 Глава 2 Низкий уровенъ Приведенная процедура является развитием процедуры, пока­ занной в предыдущем параграфе, на случай реальноrо времени. Она требует понимания, как перепрограммиров~ть прерывание таймера, что обсуждалось в (2.1 .7). На :пу процедуру должен ука­ зывать вектор прерывания и тогда она будет выполняться 18,2 раза в секунду в те моменты, когда будет обновляться значение счетчика времени суток BIOS. Обычно выполняется только несколько строчек, которых достаточно, чтобы определить, что время изменения звука еще не наступило и процедура освободит процессор для решения других задач. Счетчик времени суток BIOS применяется для измерения дли­ тельности каждой ноты. При переходе от одной ноты к друrой дли­ тельность новой ноты вычисляется как число импульсов счетчика и это значение добавляется к текущему ero значению. Каждый раз при вызове процедуры проверяется текущее значение счетчика времени суток, и когда ожидаемое время на.JЮНец наступает, выполняется набор операций по поиску новой ноты, програм­ мированию ее частоты в канале 2 микросхемы 8253 и установ­ лению новоrо счетчика длительности. Добавочный код требуется для обработки специальных случаев первой и последней нот в строке. ;---в сегменте данных ВЕАТ FREQ MELODY HOLDIP HOLDCS DB 10,9,8,7,6,5,4,3,2 ;дш1телыюсть нот DW2280;2031,1809,1709 ;таблица частот DW 1521,1355,1207,1139 DB l ,2,3,4,5,6,7 ,8,0FFH ;номер •шстоты в табшще DW0 . DW0 ;запомш_1аем ориn-шаль111,1й ;вектор прерьша1ши SOU_NOW? DB 1 FlR_NOTE? DB 1 END _NOTE DW О WHICH_NODW0 ;звук вклю•1е11? ;первая 11ота? ;счет•~ик ко11ца 1юп,1 ;указа1·сJ11, 1111 текущую 1юту ;---инициализация вектора прерывании ;изменение вектора PUSHDS ;сохраняем регистр MOV AX,SEG MEL2 ;сегмент процедуры MOV DS,AX ;помеuщем в DS MOV DX,OFFSET MEL2 ;смещеш1е процедуры MOV AL,lCH ;номер вектора прерьшш~ия
_таймеры и звук MOV AH,2.SH INT 21Н РОР DS ;функция уста1ювк11 вектора ;изме11ение вектора ;восстаноw1еш1е регистра ;---программа работает дальше, постоян~ю вызывая про11е-дуру ;---в конце программы восстанавливаем ltектор прерыщшня MOV DX,0FFSЗH ;восста1~аwшваем ор~1rнналы1ые MOV AX,0FOOOH MOV DS,AX MOV AL,ICH MOV AH,2SH INT 2IН RET ;---это само прерываш1е MEL2 PROC FAR PUSH АХ PUSH ВХ PUSH СХ PUSH DX PUSHDI PUSH SI PUSH DS MOV AX,SS:[114) MOV DS,AX СМР SOU_NOW'!,1 ;зна•1ен11я для вектора I СИ ;11омер прерьша~тя ;фу11кция уста11011ки вектора ;восстанавливаем вектор ;сохр:шяем нзме1111емые реп1стр1,1 ;берем ш1 1шm,ный DS со стека ;восста11а1u1ивасм е1·0 ;нуже11 ;ш звук? 101 JE PLAY 1Т ;если 11ет, то IIЫХОД flЗ 11рер1,1щ111ш1 JMP NOT_NOW PLAY_IТ: СМР FIR_NOTE?,0 JE ТIМ СНК ;---инициализация PORT В EQU 6IН COM_REGEQU 43Н LATCH2 EQU 42Н IN AL,PORT_B OR AL,000000118 оuт PORT_B,Лl, MOV S1,0 MOV AL,0BЫI ;это 11ер11ая 11ота '! ;ecлit нет, то на уста~ювку 11рсмеш1 ;определяем имена 1щрто11 ;берем статус ,юрта В ;разрс111:1ем днш1м~1к 11 таймер ;1юсыласм байт обратно ;указатсл1а 11а строки ;инн1tнал1-1зацнw ка11ала 2 паr1мсра
102 Глава 2 OUT COM_REG,ЛL ;11осыJ1аем 11 ком1111дш,1й рсп1стр MOV FIR_NOTE?,0 :сбрас1,111аем фж11· 11ер1юй шпы ;---ищем ноту, получаем ее •iac·roтy, посыJ1аем в к:111аJ1 2 NEXT: LEA BX,MELODY MOV Sl,WHICH_NO MOV ~L. [BXJ [Sl] СМР AL,0FFH JE NO_MOR свw ;получаем частоту MOV BX,OFFSET FREQ DEC АХ SHL AX,l MOV Dl,AX MOV DX, [ВХ] [DI] ;начинаем исполнение ноты MOV AL,DL OUT LATCH2,AL MOV AL,DH OUT LATCJ-12,Л.L ;берем смещс1ше строки мСJюдш1 ;указатель на текущую ноту ;код текущей ноты строк .. ;проверяем приз11ак конца ;если да, то на ко11ец ;~шаче - в сло1шый формат ;смещсш1е табшщы частот . ;11а•шнаем отс1 1ет с 11уля ;ум11ожаем iia 2, так как с1ю11ш1я -табшща ;адресуемся •1ерсз DI ;полу•1аем •~астоту ~•з таблицы ;готовим млад11шr1 б.айт частоты ;посылаем в регистр зад1шжк~1 ;готовим стар~ш1й байт ;nOC1,1J18CM е1"0' ;---пустой цикл, ощrеде11яющ~•~· ДJШTCJll,IIOCП, ll()T ТIМЕ lT: MOV ЛН,О INT IЛН MOV BX,OГ-l'Sl::T ВЕЛТ MOV CL, [В.Х] ISI] моvсн.о MOVBX,DX ADD ВХ,СХ MOV END_NOTE,BX ТIМ_СНК: MOV АН,О INT IAI-1 СМР DX,END _NOTE JNE NO_NOW MOV Sl,WНICI-I_NO INC Sl MOV WfHCI-I_NO,Sl JMP NEXT__ NOTE ;---завершение про~едуры NO_MOR: IN AL,PORT_B . ;фy1tKLLI-IW 11·1:CIII-IS>I счст 11ик11 ;11олу11;1см з1ш11t..~11ис с 1 1с·р11-1кн ;смсщс1шс строки щшн нот ;дл1-пслы1ост1, TCKYIILCЙ ll(ПЪI ;м1111дшсс CJIOIIO ЗIШ'ICIIШI с•1ст•1ика ;добаw1ясм дщшу 11 имнут,сах ;зшюм~шасм uрсм11 око11 1ш11ия ;фу11кция •пс1шя с•1ст•~ика ;1.1итасм с1.1етч1-1к ;сршшшшсм с нужным ;ССJШ 11сра111ю, то 111,IХ(ЩИМ ;ш1а•1с, берем слс;1ующую ,юту ;у1sсли 1 11-11шсм ll()M~p IIOTЫ ;зшюм~шасм ст ;берем статус порта В
Таймеры и звук AND AL,0FCH OUT бlН,АL MOV SOU_NOW?,0 ~ ov FIR_NOTE?,I NO_NOW: РОР DS РОР SI РОР DI РОР DX РОР СХ РОР ВХ РОР АХ IRET MELODY2 ENDP ;оыклIочаем д,5,1I~амик ;возвращаем байт ;восстанавливаем IIсремеI1111,1с ;оосста11а11л~шаем регистры ;uозuрат из прсрI,II~аш1я 1.1.7. Создание плавного перехода тонов 103 Плавные переходы тонов производятся за счет непрерывного изменения частоты. Этого можно достигнуть как в Бейсике, так и программируя на ассемблере. Такой звук"овой :>ффект можно сде­ лать более выразительным, если немного умею,шап, д.1нпел1,ност1, каждого сегмента тона при повышении звука или слегка увели­ чивать длительность при понижении. Высокий уровень В Бейсике надо просто поместить оператор SOUND 12.2 .2] в цикл, используя очен1, малые длины тонов. При каждом новом проходе цикла надо увеличивап, частоту. В 12.2.8 1 приведен пример использования оператора PLA У для более быстрых переходов. 100l'ORN = 1ТО500STEI'15 110 SOUND 400 + N,I 120 NEXT Низкий уровень Проще всего применять метод генерации звука, управляемый микросхемой интерфейса с периферией 8255. Меняйте значение бита 1 порта В между О и 1, используя для отсчета времени пустой цикл, как показано в [2.2 .2 1. Пр~ начале каждого нового
104 Глава 2 пустого цикла, за счет засылки значения в СХ, слегка изменяйте это значение. Здесь тон повышается: ;---запрет микросхемы таймера РВ .EQU 61Н ;адрес ,юрта В микросхемы 8255 IN AL.PB, OR AL,l OUT PB,AL ;получаем из 1Iero байт ;сбрасываем бит О ;возвращаем байт в порт ;---установка частоты и длительности звука MOV ВХ,9000 MOV DX,3000 REPEAT: ;начальное значение счет•шка ;длител1шость звука 3000 циклоп ;сюда Iюзвращаемся после цикла ;---установка бита дщшмика OR AL,00000010B ;уста11апш111нем бит 1 OUT PB.AL MOV СХ,ВХ CYCI:LOOPCYCI ;---сброс б:1та динамика ;посылаем байт 11 порт n ;устаIюпка счетчика для 1/2 ЦflKJШ ;пустой цикл 1111 1ОО? rюIпоро11 AND AL,l l l l l IOIВ ;сбрасываем бит 1 OUT PB,AL MOV СХ,ВХ СУС2: LOOP СУС2 ;посылаем байт п порт ;установка счетчика ;пустой цикл ;---переход к следующему циклу DEC ВХ ;увеличиваем частоту, уме111,II~аи DEC ВХ DEC DX JNZ REPEAT ;счетчик ;уменьшаем остапшуюси длfпслI,Iюсп, ;если DX не О, то 1юш,1й цикл .. Этот простой метод приводит к тому, что высокие тона про­ ходят значительно быстрее, чем низкие.. Для коротких интервалов такой эффект может быть желательным, а когда он не нужен, надо добавить код, который при повышении тона пересылает в DX большие значения на следующем цикле. 2.2 .8 . Создание звуковых эффектов Звуковые эффекты обычно достигаются непрерывным измене­ нием частоты тона. Только PCjr достаточно хорошо оборудован для этой цели (см. [2.2, 1 ]) . На остальных машинах нельзя произ­ водить звуковые эффекты одновременно с другими операциями.
Таймеры и звук 105 Высокий уровень Блаrодаря мощности операторов SOUND и PLAY Бейсик позво­ ляет достаточно легко созд~вать сложн1>1е звуковые :эффекты. Но все должно быть сконструировано из чистых музыкальных тонов, а это значит, что эффект дисторции звука должен достигаться за счет такоrо быстроrо изменения тона, что ухо не успевает разде­ лить тона. Например, душераздирающее "чириканье" может быть получено при быстром переключении между одним и тем же тоном, отстоящим на несколько о~тав: 100FORN=1ТОНЮ 110 PLAY "L64 Т255" 120 PLAY "OlA" 130 PLAY "OSA" 140 NEXT ·установка дл~пелыюсп1 ·самый быстрый темп ·выдаем низкое Л •111,щаем •,11,1сокос Л ПОIIТОр При изменении частоты всего на несколько герц получаем вибрацию: 100FORN=1ТО100 110 SOUND 440,1 120 SOUND 445,1 130 NEXT ·усп11ю11ка 1uште;1ыюсп1 ·11ы11аем ~юту Л 11ем1ю1·0 меш1ем •шстоту IIOIПOp Другой метод заключается во вложении плавно меняющихся тонов внутрь последовательности, которая сама "гуляет" по ,час­ тотам вверх или вниз. На рис. 2.6. показана движущаяся вверх пор~едовательность. Этот метод применяется во многиf играх с лабиринтами. • 100FOR1=1ТОl(' 110FORJ=1ТО6 11исло nовтореш1й •шсло разных октав 120 PLAУ "mЫ64t255O = j;ba #ag #gf #fed #dc #се #dd #cff #gg #аа #Ь" • 130 NEXT ·поптор в более n1,1сокой окта11с 140 NЕхт· ·повтор всей последопатслыюсти PCjr :значительно более мощный, чем остальные машины, бN;~аrодаря специальной микросхеме генератора звука. Оператор OISE может производить мнqжество звуков, формат этого с:>nератора такой: N.OISE _источник, громкость, длительность
106 Глава 2 Источник - это число от О до 7, значение которого приведено в таблице: о 1 2 3 4 5 6 7 nepflOДИ•leCKflЙ шум II Bl>ICOKOM диа11азо11е nериОДИЧССКflЙ шум в среднем Дfl:НШЗОIIС периодический шум в низком Дf1аr1азонс периодf1ческий шум, диапазо11 мс1111ется с каналом 3 белый шум в высоком диапазоне белый шум в сред11см диаnазо11е белый шум в низком диапазоне белый шум,диапазо11 меняется с каналом 3 Громкость задается числом от О до 15, где О соответствует отсутствию звука. Длительность указывается числом импульсов счетчи~а времени суток, которые отсчитываются 18,2 раза в секунду. Рис. 2.6 . Звуковой эффект скользящеl'О тощ, -- Низкий уровень Любой реализован параграфов, из способов, показанных на Бейсике, может быть на ассемблере, хотя, как видно из предыдущих это требует затрат на программирование. Кроме того,
Таймеры и' звук 107 ассемблер позволяет rснсрироват1, нечистые тона. коrда интервал, в течение котороrо динамик включен, нс равен интервалу, в течение котороrо он выключен. Такое нарушение симметрии •может приводить к жужжащим и брякающим звукам. Коrда отношение этих интервалов составляет, скажем, 50 к 1, получаем жужжание. Если увеличить отношение еще в• 1О - 20 раз, то жужжание переходит в отдельные брякающие звуки. В любом случае звук генерируется микросхемой интерфейса с периферией 8255 с помощью методов, показанных в (2.2.2 ]. Вот пример жужжания: NUM_CES FREQI FREQ2 PORT_8 NEXT CLE: ВQЧ 300 EQU 50 EQU 3200 EQU б!Н ;число переклю•1е11нf1 д~111амнка ;время, когда ди1~амик 11к111011е11 ;время, КОl'дП. ДИIШМНК BЫKJIIOЧCII ;адрес ~юрта ·В микросхемы 11255 CLI моv IN AND OR ;занрст 11рер1,1щ111ий DX,NUM_CES ;DX с•шп1ет дш111у п!11а AL,PORT_8 ;I10лу•шем статус ,юрта , AL, 111111108 ;отключаем ди11ам~Iк от таймера AL,000000I0B ;I1клю•~аем динамик OUT PORT_B,ЛJ, ;1юсылаем комаIщу MOV CX,l'REQI ;задержка для первой •шспI f'IRST HЛJ,F: 1,ООР FIRST I-IALf' - - AND ЛI,, 11111101 В ;111,Iключаем диIшмик OUT PORT_B,Лt, ;11осыласм ком:111:tу MOV cx,1:REQ2 ;задержка дшI второii •~асп1 SEC HALF: LOOPSECND_IIЛl,J' DEC DX JNZ NEXT CLE SТI ;уме1н,11jасм число н~1кJюв ;если О, то нора зака~1115-11sат1, ;разрешаем I1рсры11ашIя Для создания брякающих звуков можно использовать этот же код, но надо заменить значение FREQUENCY2 на величину около . 40000. 1.1.9. Одновременная rенерацня разных звуков Только микросхема генератора звука, имеющаяся в PCjr, способна одновременно генерировап, разные звуки (см. t2.2 .l ]). Однако ассемблер позволяет объединить два способа генерации звука, что создает имитацию одновременной rснсрации двух •
108 Глава 2 разных звуков. Интерференция этих двух сигналов приводит к сложной форме звуковой волны. Каждый из двух звуков имеет меньшую громкость, поэтому в результате получается скорее жужжание, чем два разных голоса. Этот прием реально полезен только для создания звуковых эффектов. Низкий уровень Надо просто объединить два метода генерации звука, показанные в [2.2 .2] и [2.'2.3 ]. Начните звук через канал 2 микросхемы таймера. Затем модулируйте выход динамика за счет бита 1 порта В микросхемы интерфейса с периферией. Второе действие определяет продолжительность звука. Не забудьте выключить микросхему таймера при завершении. ;---начинаем генерацию звука через ка~шл 2 таймера IN АL,61Н ;получаем байт из порта В OR Al,,3 ;уставаw1ивасм млад111ис дна байта оuт 61Н,АL ;rIосыласм байт обрап10 MOV AL,I0I HH I0B ;цс1ючка JtJJЯ KOM,IIJДJIOl'O pel'fJCтpa 11253 оuт 43Н,ЛL ;11осылаем II реп1стр MOV АХ,600Н ;счетчf1к для кат1ла 2 OUT 42H(AL ;посылаем млад1ш1й байт моv AL,AH ;готовf1м старший байт OUT 42H,AL ;посылаем стар1ш1й байт ;---гецерируем вторую частоту микросхемой 8255 NUM_CES EQU 9000 ;число 11ереклю 1 1еш1й FREQ EQU 150 ;задержка 11.пя 1ю1ю1m111,1 1tf1кла CLI моv DX,NUM CES - IN AL,61H AND AL,1111111 IB NEX_CLE: OR Al,,0000001 ОВ OUT 61H,AL MOV CX,FREQ FIR НЛF: LOOPFIR_HAF AND AL,111111018 • OUT 61Н,АL MOV CX,FREQ SEC_HAF: LOOPSEC_HAr' DEC DX ;за~1рет 11рер1,1щ1ш1ii ;ОХ С•1fпает )tШIIIY TOllil ;1юлучасм статус 11орт~1 ;отк;11011асм 1,1-1iшм1-1к от таймера ;нкл1очасм ди1н1м1-1к ;11осыласм 1шза,1 t в 11орт ;задержка на 1/2 11ик;ш ;выключаем д1-11шм1-1к ;пqсылаем кома~щу 11 ~юрт ;задержка на 1/2 Нf1к1ш ;меII~см счетчик н1-1клов
Таймеры и звук 109 JNZ NEX CLE ;если О, то r1opa зака11ч1-1щ~·1ъ STI ;разрешаем 11рер1,ша11и11 ;---выключение канала 2 м~1кросхемы таймера IN AL,61 Н ;1юлу•rаем статус ~юрта AND AL, 111 11 1ООВ ;сбрасыuаем 2 мл.1111ш1х бита OUT 6/Н,АL ;посылаем байт обратно
Глава 3. Клавиатура 3.1. Управление клавнатурон Клавиатура содержит интелсвский микропро­ цессор, который воспринимает каждое нажатие на клавишу и выдает скан-код в порт А микросхемы интерфейса с периферией [ 1.1 .1 ], расположенной на системной плате. Скан-код - :по одно­ байтовое число, младшие 7 бит которого представляют идентифи­ кационный номер, присвоенный каждой клавише. Таблица скан­ кодов приведена в [3.3.2 ]. На всех машинах, кроме АТ, старший бит кода говорит о том,· была ли клавиша наж.~та (бит = 1, код нажатия) или освобождена (бит О, код освобождения). Например, 7-битовый. скан-код клавиши В - 48, ил1t 110000 в двоичной системе. Когда эту клавишу нажимают, в порт А посылается код 10110000, а когда отпvскают - код 00110000. Таким образом, каждое нажатие • на клавишу дважды. регистрируется в микросхеме 8255. И всякий раз микросхема 8255 выдает подтверждение микропроцессору клавиатуры. АТ работает немного по-другому, посылая в обоих случаях один и тот же скан­ код, но предваряя его кодом F0H, если клавиша отпускается. Когда скан-код выдается в порт А, вызывается прерывание клавиатуры (INT 9). Процессор моментал1,но прекращает свою работу и выполняет процедуру, анйлизирующую скан-код. При поступлении кода от клавиши сдвига или переключателя изменение статуса записывается в памят1,. Во всех остал~,ных случаях скан-код трансформируется в код символа при условии, что он подается при нажатии клавиши (в противном случае скан­ код отбрасывается). Конечно, процедура сначаж1 определяет установку клавиш сдвига и переключателей, чтобы правильно
К.rшвиатура 111 получить вводимый код (это "а" или "А"?). Затем введенный код помещается в буфер клавиатуры, который является областью памяти, способной запомнить до 15 вводимых символов, пока программа слишком занята, чтобы обработать их. На рис. 3.1 показан путь, который проходит нажатие на клавишу перед тем, как попасть в вашу программу. Имеются два типа кодов символов: коды ASCI I и расширенные коды. Коды ASCII - это байтовые числа, соответствующие расши­ ренному набору .кодов ASCII для IВМ РС, приведенному в [3.3.3 J. Для IВМ РС этот набор включает обычные символы пишущей машинки, а также ряд специальных букв и символов псевдогра­ фики. Коды ASCII включают также 32 управляющих кода, кото­ рые обычно используются для передачи команд периферийным_ устройствам, а не выводятся как символы на экране; однако каж­ дый из них имеет соответствующий символ, который может быть выведен на дисплей, с использованием прямой адресации дисплей­ ной памяти [4.3 .1 ). (Строго говоря, только первые 128 символов являются настоящими символами ASCII, так как АSСП - это аббревиатура, означающая American Standard Code for lnformation Interchange (американский стандартный код для обмена информа­ цией). Но программисты обычно говорят о кодах ASCII, чтобы отличить их от других чисел. Например, ASCII 8 относится к кла­ више <<Backspace,>, в то время как 8 - это цифра, которой соответ­ ствует ASCII 56& .) Вrорой набор кодов, расширенные коды; присвоен клавишам или комбинациям клавиш, которые не имеют представляющего их символа ASCII, таким, как функциональные клавиши или комби­ нации с клавишей Alt. Расширенные коды имеют длину 2 байта, причем первый байт всегда ASCII О. Второй байт - номер расши­ ренного кода (список кодов приведен в (3.3.5 J>. Например, код 0:30 представляет Alt-A. Начал'ьный ноль позволяет программе определить, принадлежит ли данный код набору ASCII или расши­ ренному набору. Существует нсскол1,ко комбинаций кж1виш для выполнения специальных функций, они нс генерируют скан-коды. Эти комби­ нации включают «Ctrl-Brcak,>, <<Ctrl-Alt-Del,> и <<PrtSc,> плюс «SysReq,> для АТ и <<Ctrl-Alt-cтpcлкa влево, -стрелка . вправо, - CapsLock, -lns>> для PCjr. Эти исключения приводят к заранее предопределенным результатам [3.3.2 [. Все остал1,ныс нажатия клавиш должны интерп·ретироваться вашей программой, и сели они имеют специальное назначение, скажем сдвинуть курсор влево, то ваша программа должна содержат~, код для достижения этого эффекта. К счастью, операционная система позволяет выполнять различ­ ные проμ.едуры для чтения кодов из буфера клавиатуры, включая
112 8048 Процессор клавиатуры 8255 Интерфейсе периферией Прерывание !РJ&виатуры (записанное • ПЗУ) Буфер !!давиатуры (системнu паМ11Ть) Прерывание 8Ы80АаНа 311ран В11Аеобуфер KoAыASCII 11 _расширенные КОАЫ КоА программы □.о Рис. 3.1. От клавиатуры к экрш1у 1 Глава 3 средства для получения сразу целой строки. Поскольку эти проце- дуры обеспечивают практически все, что вы можете пожел,tть, бессмысленно писать свои програмft!Ы обработки ввода с клавиатуры, и поэтому в данной rлаве очень мало примеров программирования на низком уровне. В ней обсуждаются вопросы, . связанные с перепрограммированием прерывания клавиатуры. 3.1.1 . Очистка буфера кnавнатурьt Программа должна очистить буфер клавиатуры перед тем, как выдать заnрос на ввод, исключая тем самым посторонние нажатия клавиш, которые могут к тому времени накопиться в буфере. •
Клавиатура 113 Буфер может накапливать до 15 нажатий на клавишу независимо от того, являются они однобайтовыми кодами ASCII или двухбай­ товыми расширенными кодами. Таким образом, буфер должен отвести два байта памяти для каждого нажатю, на кла,вишу. Для однобайтовых кодов первый байт содержит код ASC! i, а второй - скан-код клавиши. Для расширенных кодов первый бачт содержит ASCII О, а второй - номер расширенного кода. Этот код обычно совпадает со скан-кодом клавиши, но нс вссrда, поскол1,:,у неко­ торые клавиши можно комбиниJJ(>вать ·с клавишам,1 сдзиrа для генерации различных кодов. Буфер устроен, как ци1,,.лн,,..,t:t..,1:А очередь, которую наз·ыва~с1 также буфером FIFO (первый вошел - первый ушел). Он з;~нимаст непрерывную область адресов намяти. Однако определенной ячейки памяти, которая хранит "начало строки" в буфере, нет. Вместо этого два указателя хранят позиции rолоRы и хвоста строки символов, находящейся в буфе'ре в текущий момент. Новые нажатия клавиш сохраняются в позициях, с,,сдующих за хвостом "(в бо.т;~с старших адресах памяти), и соответственно обновляется указат~,1ь хвоста буфера. После того как израсходовано вес буфер­ ное п;юстранство, новые символы продолжают вставляп,ся, начи·­ ная с самого начала буферной области; по;пому возможны ситу­ ации, ;шrда голова строки в буфере 11мсст бол~,ший адрес, чем хвост. I(огда буфер заполнен, новые вводимые символы игнорн­ руютсs;:, при этом прерывание клавиатуры выдаст гудок через динамик. На рис. 3.2 показаны некоторые возможные конфиrу- рации данных в буфере. ' В то время как указатель на голову установлен н,1 первый введенный символ, указатель на хвост установлен на позицию за последним . введенным симв(.')лом. Коrда оба указателя рзнны,, буфер пуст. Чтобы разрешить ввод 15 символов, требуется 16-я пустая, позиция, 2 байта которой. всегда содержат код возRрата каретки (ASCII 13) и скан-код клавиши <<EntcГ>>, равный 28. Эта пустая позиция непосредственно предшествует голове строки сим­ волов. 32 байта буфера начинаются с адреса 0040:001 Е. У ка зател и на голову и хвост расположены по адресам 0040:00 l А и 0040:001 С соответственно. Хотя под vказатели отведено 2 байта, используется только младший байт. Значения указателей меня­ ются от 30 до 60, что соответствует позициям в области данных BIOS. Для очистки буфера надо просто установить значение ячейки 0040:00lA равным значению ячейки 0040:00lC. Отметим, что программа может вставляп, символы в буфер, завершая строку символом возврата каретки и меняя значения указателей. Если это сделано перед завершением проrраммы, то при возврате управления в MS-DOS эти символы будут счита'ны и может быть автоматически загружена друrая проrрамма.
114 0040:ООЗС ЗА 38 36 34 ·в· 32 ·u· 30 'F' 21; 'F' 2СЕ 2А 'R' 28 26 24 22 20 1Е 1С 0040:001А \ Указатель хвоста Указатель головы 'F' 'F' 'Е' 'R' ·в· ·u· Рис. 3.2. Конфигурация буфера клавнатур1,1 В,ысокий 'уровень Глава 3 В Бейсике для получения и изменения значений указателей буфера используются операторы РЕЕК и РОКЕ: 1()0 DEF.SEG = 1 &Н40 110 РОКЕ &НIС, РЕЕК(&НIА) ·устанаw,~шаем з11а•1еш1е се1·ме1па вырашшпаем указатели Этот метод не самый лучший. Некоторые программы могут создавать буфер где-нибудь в другом месте памяти, а кроме того, всегда существует возможность, прерывания _клавиатуры в сере­ дине строки. 110, которое изменит указа гель хвоста. По этим при­ чинам лучше оставить указатели буфера в покое. Нужно читать из
Клавиатура 115 бvфсра до тех пор, пока нс будет нознращсн симнол ASCII О, показывающий, что буфер пуст: 100 11° INKEY$<>"" THEN 100 Средний уровень ·берем сле;1ую111ес, сели 11е 11у;11, Функция ОС прерывания 21Н выполняет любую из функций ввода с клавиатуры 1, 6, 7, 8 и А (описанных в данной главе), но перед этим чистит буфер клавиатуры. Надо просто поместить номер функции ввода в AL (в этом примере - 1): ;---очистка буфера перед ОЖfщаш1ем 1шжап1я кл:шf1111и MOV АН.ОСН ;в1,1бирасм фу11к1tf11О DOS OCI 1 MOV AL,I INT 21Н Низкий уровень ;выб5-1расм фу11к1tи.ю IШО)Ш CI-IMBOJla ;чf1стим буфер, жпсм 1111ода Как и в примере высокого уровня, приравниваем значение ука­ зателя на хвост значению указателя на голову. Для избежания влияния прерывания клавиатуры запрещаем прерывания на время модификации указателя: ;---uырав1н-ншем з1шчс1в-н1 указат~лсй 1ш ,·олову и хнос1 CLI ;за11рсщасм 11рср1,11ш11ия SUB АХ.АХ ;об11улясм ре1·нстр MOV ES,AX ;добаuо•1111,1й сс1·мс11т - с начала 11ам11п1 ·M O V AL,ES:[41AН] ;берем указi1тель щ1 гожi11у буф~ра MOV ES:[41CH],AL ;посылаем его в указатсл1, хвоста STI ;разрешаем nреры11аш1я 3.1 .1. Проверка символов в буфере Вы можете проверить, был ли ввод с клавиатуры, не удаляя символ из буфера ·клавиатуры. Буфер использует два указателя, которые отмечают голову и хво<ст очереди символов, находящихся в буфере в текущий момент. Когда значения этих указателей равны, буфер пуст. Надо просто сравнить содержимое ячеек памяти 0040:00lA и 0040:00lC. (Нельзя просто проверить символ, находящийся в голове очереди, поскольку буфер организован в виде циклической очереди и позиция ее головы• постоянно меняется [3.1 .1 ].)
116 Глава 3 Высокий уровень Надо просто использовать оператор РЕЕК для получения значений, а затем сравнить их: 100 DEF SEG = &Н40 ·устанавливаем сегмент на 1~а•шло памяти 110 IF РЕЕК(&Н\А)<>РЕЕК(&Н\С) ПIEN ... •. .. то буфер ,,е 11уст Средний уровень Функция ОВН прерывания 21Н возвращает значение 0FFH в регистре AL, когда буфер клавиатуры содержит один или более символов, и значение О, когда буфер пуст: ;---проверка наличия символа в буфере MOV АН,ОВН ;номер фу11кцш1 INT 21Н ;вызы11аем 11рерывюmе 21 Н СМР AL,0FFH ;сравниваем с 0FFH JE GET_KEYSTROKE ;переход, есшI буфер не пуст Функция nрерывания BIOS lбН предоставляет ту же возможность, но, кроме того, она показывает, какой символ в буфере. Флаг нуля (ZF) сбрасывается, если буфер пуст, и устанавливается, если в буфере есть символ. В последнем случае копия символа, находящегося в голове буфера, помещается в АХ, но символ из буфера не удаляется. В AL возвращается код символа для однобайтовых символов ASCII, иначе ASCII О для расширенных кодов, и 'Гогда номер кода пересылается в АН.· ;---проверяем налн•нIе символа в буфере MOV АН,\ ;номер функ,~ш• INT 16Н JZ NO _CHЛR.ACTl:R ;пронерка наJ1~11,н11 снм11011а ;r,ереход. ecJJи ZI' = 1 ;---имеется символ - смотрим какой СМР AL,0 ;это рас1ш1ре1111ый ко;~? JE EXTENDED CODE ;ecJJн да. то на дру,~·ю нетку Низкий уровень Как и в примере высокого уровня,сравниваем указатс.,1и: ;--:сравниваем указатели на I·0.1ову н х11осI MOV АХ,О ;успша11лн11асм ,юбашр1111,1й сег:-1сIп
Клавиатура MOV ES,AX MOV AL,ES:[41AHJ MOV АН,ЕS:[41СН) СМР AH,AL ;ва 11ачало 1шмят1-1 ;берем од1-111 указатс;11) ;берем другой указате,11, ;сра11ншшем нх JNE GET_KEYSTROKE ;если 11ераш11,I, то к процс;tурс шю;щ 3.1 .3 . Ожидание ввода симвоnа без вывода его на экран 117 Обычно вводимые символы выводятся на экран, чтобы было видно, что напечатано. Но иногда автоматическое эхо на экране нежелательно. Например, выбор пункта меню по нажатию кла­ виши. Часто надо сначала проверить вводимые символы на ошибку перед выводом на экран. В частности, любая программа, обрабаты­ вающая расширенные коды, должна избегать автоматического эха, так как при этом первый байт этих кодов (ASCII 0) будет выво­ диться на экран, вставляя пробелы между символ;~ми. Высокий уровень Функция Бейсика INKEY$ нс дает эха на термина,,. Она возв­ ращает строку длиной в 1 байт для символов ASCfl и д,111ной в 2 байта для расширенных кодов. INKEY$ не ожидает нажатия кла­ виши до тех пор, пока она не помещена в цикл, в котором ожида­ ется нажатие клавиши. Цикл работает, обращаясь к INKEY$, а затем присваивая возвращаемую им строку переменной, в данном случае С$. Если КЛ'авиша не была нажата, то INKEY$ возвращает • нулевую строку, т.е. строку длиной в ною, симво,,ов, которая обоз­ начается двумя знаками кавычек, между котор1,1м11 н11чего нет ("" .>. До тех пор пока INKEY$ возвращает ····, щ1кл повторяется: 100 С$= JNKEY$:IF С_$= .... THI::N 100 В приведенном. ниже примере предполагается, что ввод11мыс символы выбирают одну из возможностей меню и каждый выбор приводит к выполнению определенной процедуры программы. Выбор может быть сделан за счет нажатия к,1ав11ш А, В, С ... , (давая ]-байтовые коды АSСШ или Alt-A, Alt-B, Alt-C ... (давая 2-байтовые расширенные коды). Для 11х распознаван11я 11спо,11,­ зуется функция LEN, которая определяет, была ,111 строка д,111ноii в I или 2 байта. Пр11 кодах ASCII набор операторов IF ... THEN ср,1зу начинает проверять, какая клав11ша бы.,а нажата. отсы,,ая программу на соответствующую процедуру. Пр11 2-баiiтовых кодах управлеюiс передастся отдсл1,ноii процедуре. В :.пoii процедуре функция RIGHT$ убирает ,,свыii с11мво,1, которыii просто равен
118 Глава З нулю, и только отмечает расширенный код. Затем исполI,зуется функция ASC для преобразования строки из симво,1ьной формы в числовую. И наконец, вторая серия операторов IF... THEN прове­ ряет получившееся число на соответствие кодам Alt-A, Alt-B и т.д. 100 С$ = INKEY$:IF С$='"' ПIEN 100 ·ожидаем 11ажанIи к:Iави11Iн 110 IF LEN(C$)_ = 2 THEN 500 ·есшt расшире1111ый код - Iш 500 120 IF С$= "а" OR С$= "А" THEN GOSUB 1100 ·это А? 130 IF С$= "Ь" OR С$= "В" THEN GOSUfl 1200 ·это fl'' 140 IF С$= "с" OR С$= "С" THEN GOSLfl 1300 ·это С? 500 С$= RIGHT$(C$, 1) получаем второй байт расIIIнре111юI·0 кощ1 510 С= ASC(C$) ·r1рсобра~уем еI·0 в •Iнсло 520 IF С= 30 THEN GOSUB 2100 ·з10 Л11-Л? 530 IF С= 48 THEN GOSUB 2200 ·л11-В'' 540 If С= 46 THEN GOSU[! 2300 ·л11-С'! Отметим, что' в строке 120 (и последующих) можно также использовать числовые значения кодов ASCII: 120 lf С= 97 OR С= 65 TIIE:, .. : GOSLiB 1100 Конечно, надо сначала преобразоват~, С$ в форму целого числа, как это сделано в строке 510. В программах, в которых тре­ буется дл;~нная цепочка таких операторов, можно с:жономит,, место, изменяя С таким образом, чтобы ·код буквы всегда соответ­ ствовал либо верхнему, либо нижнему регистру. Сначала нужно только проверить, что код ASCII С$ находится в прави,1I,ном диа­ пазоне. Затем установить, меньше ,1и :лот код 91, тогда мы имеем дело с символом верхнего регистра. Если :Jто так, то надо для перевода в нижний регистр добавит~, 32. В противном случае оста­ вить все как есть. После этого будет достаточно 60,1сс короткого оператора, такого, как IF С= 97 THEN ... Вот код :Jтой процедуры: 500 С= ASC(C$) Iю:Iy•Iac:vi ко;t ЛSCII с11-.,во;Iа 510 lf' NOT «С>б4 AND C<91)OR(C>96 ЛЮ) С<123>> TIIE:, .. : ... 520 lf' С<91 THEN С= С+ 32 ·11р1111(щнм вес к 11иж11сму rспю rv 53011'С =97THEN... Средний уронень Функции 7 и 8 прерывания 21 Н ожидают ввода символа, сс,1и буфер клавиатуры пуст; появление симвожI на :Jкранс нс отобра-
Клавиатура 119 жается. При этом функция 8 распознает «Ctrl-Break» (и иници­ ирует процедуру обработки <<Ctrl'-Break>> [3.2.8 ]), а функция 7 не реагирует· на неrо. В обоих случаях символ возвращается в AL. Когда AL содержит ASCII О, получен расширенный кол. Повторите прерывание, и в AL появится второй байт расширенного кода. ;---по;1у•~аем введе1111ый сим1юл моv INT СМР JE ЛН,7 2\1-1 AL.O ЕХТ COD ;номер функцш1 ;ожидаем 111юд сим1юла ;проI1ерка на рас111ирс1111ый код ;если да, то на особую нр<щедуру ;иi'шче - код сим11ола 11 ЛI, ;---процедура обработки рас11шреш1ых кодов ЕХТ COD: .INT 21 Н ;берем второй байт кода СR: СМР JNE JMP СМР Лl,,75 СR ;прт1с1нIсм на "стрслку-11лсшJ" ;если нет, то следующаw нр<шсрка CUR LEl·T ;ссш1 1щ, то 1ш IIроцснуру Лl.4, 77 ~срашн-11,асм JЩJIЫIIC и Т.}1, BIOS обеспечивает процедуру, которая предоставляет те же возможности, что и функции MS-DOS. Поместите О в АН и вызо­ вите прерывание 16Н. Функция ожидает ввода символа и возвра­ щает ero в AL. В этом случае и· расширенные коды обрабатываются за одно прерывание. Если в AL содержится О, то в АН будет содержаться номер расширенноrо кода. При этом нс обрабатыва­ ется «Ctrl-Break>>. ;---ЖJ(СМ 11ажатия KJШBИIIIH MOV All,O INT 161·1 СМР Лl.,0 н: ~,:хт СО[) ;номер фу11к11ии ож,1щ1шнI Iшо;щ ~110;1учасм 1шс11с11111.1й кон ;1Iро11срка Iш рас1ш1рс11111,1i'1 кщ1 ;сели 1ia. то на сI1с11иаю,ную 1Ip<ЩCJIYPY ;и~шчс,симIюл II ЛI, ;---процедура обработки расIш1реI11юl'О кода EXT_COD: СМР ЛН,75 ;берем рас111ирс11111,1й ко:1 щ ЛII ;1-1 т.д. 3.t .4. Ожнданне нажатня клавншн н вывод эха на экран При вводе данных и текста зхо ввод11мых символов обычно выдастся на экран. При зтом такие символы, ~1к возврат каретки "
120 Глава 3 или забой, переводятся в соответствующие перемещения курсора, а не изображаются как АSСII-символы для этих кодов. Выдача эха происходит в той позиции, где предварительно был установлен курсор, и текст автоматически переносится на следующую строку при достижении конца текущей. Перенос на следующую строку не требует специального кода, поскольку символы помещаются в сле­ дующую позицию буферной памяти дисплея, которая представляет собой одну длинную строку, включающую все 25 строк дисплея. Высокий уровень В Бейсике надо перехватить введенный символ оператором INKEY$, как показано в [3.1.З]. Затем его надо вывести на :жран, прежде чем получать таким же способом следующий. Для вывода можно использовать либо оператор PRINT, либо оператором РОКЕ прямо поместить символ в видеобуфер, с помощью отображения в - память, ка.к показано в [4.3 .1 ] <буфер начинается с сегмента памяти &НВООО для монохромного адаптера и с &НВ800 для цвет­ ного адаптера). При использовании PRINT нс забудьте nоставить в конце двоеточие, иначе (5удет автоматически добавлен код возв­ рата каретки. Ниже приведены примеры применения обоих мето­ дов. При этом не проводится никакого анализа на управляющие символы. Вщщимые сиr.,:волы .формируются в виде строки данных в переменной KEYSTROKE$. 100 • метод, использующий PRINT 110 LOCATE 10,40 120 KEYSТR$ = '"' ·установка курсора в поз~щию 10,40 ·о•шстка перемсшюй 130 С$= INKEY$:IF С$="" THEN 130 ·ож~щание шкща сим11шш 140 KEYSТR$ = KEYSTR$ + С$ 150 PRINT С$; 160 GOTO 130 100 • метод, использующий РОКЕ 110 DEF SEG = &НВООО 120 POINT = 1678 l 30 KEYSТR$ = "" ·заrшс1, е1'0 11 11срсмс1111ую 11е•1атt, CИMIIOJШ прием СJ1едующе1'0 с~1мпоJ1а ·уста1ю~ка сс1·мента на 1ш11еобуфер ·указатель 1ш позицию 10,40 очистка персмешюй 140 С$= INKEY$:IF С$="" THEN 140 ·ожидаш1е 1111ода сим1JОJ1П 150 KEYSТR$ = KEYSTR$ + С$ 160 РОКЕ POINТER,ASC(C$) 170 POINT = POINT + 2 180 GOTO 140 ·запис1, ero 11 персме1111ую ·помещеш1е с~1мпоJ1П II пидеобуфер ·сдпиг указателя на следующ~1й сим1юл ·прием следующеr'О с~1м1юла
Клавиатура 121 Средниi-! уровень Функция 1 прерывания 21 Н ожидает ввода символа, если буфер клавиатуры пуст, а затем выводит его на :>кран в текущую позицию курсора. Обрабатывается «Ctrl-Brcak,>, поэтому может выполняться процедура обработки «Ctrl-Break>> [3.2.8 ]. Введенный символ возвращается в AL. При вводе расширенного кода AL содержит ASCII О. Для получения в AL второго байта расширен­ ного кода надо повторить прерывание. ;---nолу•1ение введенного символа MOV АН,1 ;номер фу11к11ш1 INT 21Н ;ожидаем 11ажатш1 к;~а11юш1 СМР AL,0 ;расширс1111ый код'! ; JE ЕХТ_ COD ;если да, то 11а сt1сц~~ал1,11ую 11р(щсдуру ;иначе - символ 11аходится в ЛI" ;---процедура обработки расшире1111ых кщю11 СR: INT 21Н ;1ю;1у•~аем II Лl, 1юмер кщщ СМ!' AL,77 ;нро11срка на "курсор-впра~ю" JNE СR _JMP CUR RIТ СМР AL,75 ;сели IICT, нровсрка C.!ICJtYK>lltC'l'O ;если да, то на 11р<щснуру ; ... И Т.}1. Эта функция полностью игнорирует клавишу <<ESC,>. Кл_авиша табуляции интерпретируется нормально. Клавиша «Backspace>> сдвигает курсор на одну позицию влево, но символ, находящийся в этой позиции, не стирается. Клавиша «Entcr» вызывает переме­ щеi-ше курсора в первую позицию текущей строки (нет автомати­ ческого перевода строю,,). 3.1.5 . Прнем снмвоnа без ожндання Некоторые программы, работающие в реальном времени, нс могут останавливаться и ждать нажатня клавиши; они принимают символ из буфера клавиатуры только в те моменты, когда это удобно для программы. Например, бездействие процессора во время ожидания ввода с клавиатуры остановило бы все действия на экране в игровой программе. Напомним, что с помощью методов, описанных в [3.1.2 ], легко проверить, пуст или нет буфер клавиатуры. .
122 Глана 3 ВЫСQ!<ИЙ уровень Надо просто использовать INKEY$, не помещая его н цикл: 100 С$= INKEY$ 110 IГ С$ <> .... TIIEN ... 120 ... Средний уро11ень ·11олу 1 1с11ис с5-1мво~·~а ·сели С5-1мш,л ввс;н.~11. Н) ... ·5-11н1 1 1с - нет си~во:~а в буфере Функция 6 прерывания 21 Н - это сдинстненный способ полу­ чить введенный символ без ожидания. Эта функция нс даст эха на экран и не распознает «Ctrl-Break>>. Перед вызовом прерывания в DL должно быть помещено 0FFH. В друrом случае функция 6 слу­ жит совершенно противоположной цели - печатает в текущей позиции курсора символ, находящийся в DL. Флаг нуля устанав­ ливается в l, если буфер клавиатуры пуст. Если символ принят, то он помещается в AL. Код ASCII О индицирует расширенный код, и для получения номера кода прерывание должно быт1, понторено. моv MOV INT JZ СМР JE ЕХТ COD: INT ЛН.6 DL,,0l·TH 2111 NU CJ-IAR AJ,,0 ЕХТ COD 211-1 ;номер фу11к11ш1 1)0S ;за11рос ввода ( клавиатур~.~ ;11олу 1 1с11нс снмвола ;перехо,'t, ссл1-1 нет симво~1а ;r1ро11срка ш1 рас111ирс11111,1й ко:1 ;сели да, то на с11сцнат,11ую 11р(щс11уру ;иначе - 11 AJ, код ЛSС/1 ;штучасм номер рас1ш1рс111101·0 ко;щ ;номер кода 11 AI, 3.1 .6 . Поnученне строкн снмвоnов И Бейсик, и MS-DOS предоставю1ют возможности для выпол­ нения процедур приема строки символов. Они автоматически пов­ торяют процедуры ввода одноrо символа, описанные ранее, ожидая ввода возврата каретки, сигнализирующего об окончании строки. Конечно, должна быть отведена память, достаточнщ1 для приема всех символов строки, и должна записыват1,ся длина каждой строки для того, чтобы отделить одну строку от другой. Это дела­ ется с помощью дескрипторов строки, которые состоят из одноrо или более байтов, содержащих адрес и /или длину" строки. В Бей­ сике первые два байта дескриптора строки содержат адрес строки, а сами дескрипторы хранятся в массиве отдельно от строк. Длина строки хранится в третьем байте 3-байтового дескриптора. С дру-
Клавиатуре~ 123 гой стороны, DOS хранит длину строки прsIмо в нача,1е caмoii строки, и для программы достаточно знап, по.1ожен11е строк11 в .памяти. Высокий уровень Бейсик может принимать строку как с автоматическим эхом на экране, так и без него. Более просто де,1ается ввод с эхом, так как он выполняется встроенной функцией ввода строю~ INPUT. INPUT автоматически собирает вводимые с11мво,1ы, выводя их на экран по мере получения. При нажатии клавиши <<Enter,> ввод заве!)шается, и значение строки присваивается указанной перемен­ ной (посылаемый клавишей «Enter>> код ASCII 13 нс ;юбавляется к строке). INPUT допускает возможносп, редактирования строк1-1, предоставляемую DOS, поэтому опечатки могут бып, 11справ,1ены перед вводом строки. INPUT принимает числа в виде строки 11 автоматически преобразует их в числовую форму, если для ввода будет указано имя числовой переменной. Наконец, INPUT может выдавать на экран строку, запрашивающую по,1ьзователя о требу­ емой информации. Такая строка может быт~, длиной до 254 симво­ лов. Если ее длина больше, то лишние символы игнорируются. Основная форма этого оператора INРUТ "запрос.. , имя переменной. Полное описание этого оператора см. в рvко- водстве по Бейсику. - 110 INl'UT "Ente1· y0tII· паше: ",~ЛМЕS ·11рнш1мас·1 нм11 как ороку 120 INPUT "Enter уо1II· agc: ",ЛGЕ '1/0 ·11рнш1маст 11озраст как ,,нс;ю Оператор INPUT неприменим, если в вводимой строке могvт встретиться расширенные коды; такие, как ко,1ы управ,1ен11я ку·р­ сором в полноэкранном текстовом редакторе. В этом с,1учае· требу­ ется использовать функцию INKEY$ для приема каждого символа, затем проверять ввод на расширенные коды, выде,1яп, символы управления курсором, такие, как возврат каретки, и то,1I,ко после этого выводить на экран те символы, которые следует вывод11тI,. При этом управляющие символы также вк,1ючаются по одному в конец строковой переменной. Текстовые фай,1ы представляют собой набор таких строковых переменных. В параграфе 13.1.8 1 вы найдете процедуру ввода с клавиатуры, в которой функция INKEY$ испол~,зуется указанным образе>м. Средний уровень Функция ОАН прерывания 21 Н позволяет вводить строку длиной до 254 символов, выдавая эхо на терминал. Эта проце.::1ура .
124 Глава З продолжает ввод поступающих символов до тех пор, пока не нажата клавиша возврата каретки. DS:DX указывает на адрес памяти, куда должна быть помещена строка. При входе первый байт в этой позиции должен содержать число байтов, отводимых для этой строки. После того как строка введена, второй байт даст число реально введенных символов. Сама строка начинается с третьего байта. ,_ • Надо отвести достаточно памяти для строки нужной длины плюс два байта для дескриптора строки и один добавочный байт для возврата каретки. Когда вы устанавливаете максимальную длину строки в первом байте, не забудьте добавить l для возврата каретки. Код возврата каретки - ASCII 13 - вводится как пос­ ледний символ строки, но он не учитывается в результате, кото­ рый функция помещает во второй байт дескриптора строки. Таким образом, для получения 50-символьной строки надо отвести 53 байта памяти и поместить в первый байт ASCII 51. После ввода 50 символов второй байт будет содержать ASCII 50, а 53-й байт отве­ денной памяти - ASCII 13. ;---в сегменте данных SТRING DB 53 DUP(?) ;область для строки 50 символов ;---получение строк~• с клавщ1туры LEA DX,STRING ;DS:J:?X указывают- 11:1 адрес строк~• MOV BX.DX ;пусть ВХ тоже указьшает 1ш строку MOV AL,51 ;установка длины строки ( + 1 д:1я CR) MOV [ВХ] ,AL ;посылаем в 1-й ба~п дескрнr1тора MOV АН,ОАН ;номер функцш1 IN'f 21Н ;получаем строку ;---проверка длины строки MOV АН, [ВХ] + 1 ;теперь длина в АН В этой процедуре можно использовать возможности редактиро­ вания строки MS-DOS. Нажатие клавиши <<Backspace» или "стрелка-влево" удаляет символ с экрана .без помещения·· ero в память. Работает клавиша табуляции, расширенные коды игнори­ руются, пустые строки допускаются (имеется в виду возврат каретки, которому не предшествует другой символ). На терминале при достижении правого края вводимая строка переносится на сле­ дующую строку дисплея, а при достижении правого нижнего угла экран сдвигается на строку вверх. Когда вводится ·больше симво­ лов, чем отведено места для строки, лишние символы игнориру­ ются и включается гудок динамика. M~ - DOS обеспечивает и другой способ получения строки, при котором эхо не выводится на терминал. Функция 3FH прер1,1вания 21Н - это функция ввода общего назначения, которая чаще всего
Клавиатура 125 применяется при дисковых операциях. Она требует предопределен­ ного дескриптора файла (file handle) , который является кодовым числом, используемым операционной системой для обозначения устройства ввода/вывода. Для клавиатуры использу,ется дескрип­ тор О и он должен быть nомещен в ВХ. Поместите в DS:DX адрес, по которому должна находиться строка, а в СХ - максимальную длину строки и вызовите функцию: ;---чтен~е строки без эха MOV АН,ЗFН MOV _ВХ,О LEA DX,SТR_BUFER MOV СХ,100 INT 21Н ;номер функции ;номер дескриптора файла ;указатель на буфер ввода строки ;максимальная длина строки ;ждем ввода Ввод строки завершается нажатием клавиши возвра-rа каретки и DOS добавляет в конец строки два символа: возврат каретки и перевод строки .(ASCII 13 и ASCII 10). Из-за этих добавочных символов при указании длины строки в l 00 символов она может занимать до 102 байт памяти. Длина введенной строки возвраща­ ется в АХ, и это значение включает два символа-ограничителя. 3.1 .7. Проверка/установка статуса кnавнw-переключателен Два байта, расположенные в ячейках памяти 0040:0017 и 0040:0018, содержат биты, отражающие статус клавиши сдвига и других клавиш-переключателей следующим образом: Бит Клавиiuа Значение, ког.ца бит = l 0040:0017 7 Insert режим вставки включен 6 CapsLock режиJ,1 CapsLock включен 5 No1mLock режим NнmLock включен 4 ScrollLock реж~iм ScroilLock включен 3 Alt клавиша нажата 2 Ctrl клавиша нажата l левый Shift клавиша нажата о правый Shift клавиша нажата 0040:0018 7 Insert клавиша нажата 6 CapsLock клавиша нажата 5 NнmLock клавиша нажата 4 ScrollLock клавиша нажата ' 3 Ctrl-NнmLock режим Ctrl-NнmLock включен (остальные биты не используются)
126 Глава 3 Прерывание клавиатуры немедленно обновляет эти биты ста­ туса, как только будет нажата одна из клавиш-переключателей, даже если не было считано ни одного символа из буфера клави­ атуры. Это верно и для клавиши «Ins>>, которая единственная из этих 8 клавиш помещает код в буфер (установка статуса «lnS>> меняется, даже если в б~фере нет места для символа). Отметим, что бит 3 по адресу 0040:0018 устанавливается в 1, когда дейст­ вует режим задержки <<Ctrl-Numl..ock>>; поскольку в этом состоянии программа приостановлена, ·этот бит несуществен. Прерывание клавиатуры проверяет состояние статусных битов перед тем, как интерпретировать нажатые клавиши, поэтому когда программа меняет один из этих битов, эффект тот же, что и при физическом нажатии соответствующей клавиши. Вы можете захо­ теть установить состояние клавиш <<NumLock» .и <<CapsLock>>, чтобы быть уверенным, что ввод будет требуемого вида. Наоборот, ваша программа может нуждаться в чтении статуса этих клавиш, например, для тоrо, чтобы вывести текущий статус на экран. Отметим, что клавиатура АТ правильно устанавливает световые , индикаторы состояния клавиш, даже если их статусы переключены программно. Высокий уровень В данном примере клавиша <<NumLock>> переводится в режим, когда клавиши . дополнительной клавиатуры используются для перемещения курсора в результате сбрасывания бита 5 по ддресу 0040:0017 в О. Это достигается за счет операции логического "И" значения, расположенного по этому адресу, с числом 223 (цепочка битов 11011111В, описание логики битовых операций см. в прило­ жении Б). Результат помещается в ба~т статуса. В примере затем восстанавливается значение этого бита в 1 посредством лоrи­ ческоrо "ИЛИ" с 32 (00100000В). 100 DEF SEG = &Н40 ·уста11авшшасм се1·меIп на обласп, 110 STATUSBYTE = РЕЕК(&Нl 7> ·вюs ~1 берем баfп статуса 120 NEWBYTE = STATUSBYTE ЛND 223 ·обнуляем бит 5 130 POKE(&H17,NEWBYTE) ·1юс1,1J1асм IюIюс з1I:1'1сш1с статуса Включение этого бита: 120 NEWBYTE = STATUSBYTt,: OR 32 130 РОКЕ(&Нl 7.NEWBYTE> ·ус-rа11а11;1ивасм бит 5 посы;шем 11onoe з1ш 1 1с11ис стаrуса
Клавиатура Строки 110-130 могут быть уплотнены: 110 POKE(&H417,PEEK(&H417)AND 223) или 110 РОКЕ(&Н417,РЕЕК<&Н417IOR 223) Средний уровень 127 Функция 2 прерывания lбН предоставляет доступ к одному (только к одному) из байтов • статуса. Это байт по адресу 0040:0017, который содержит больше полезной информации. Байт, возвращается в AL. ;---проверка статуса клавиши uставкн MQV. АН.2 ;1юмер фу11кцш1 INT 16Н TEST AL. 100000008 JZ INSERT_OFF Низкий уровень ;11олучасм байт t:татуса ;11ропсряем бит 7 ;если О, то INS~RT uыключе11 В данном примере устанавливается режим вставки за счет установки бита 7 байта статуса по адресу 0040:0017 (который адресуется как 0000:0417). АХ,ЛХ ES,AX Al,,IOOOOOOOB ES: [417HJ .~L ;уста1!авлиuаем добаноч111,1й се1·мс11т 11а ;начало 1шмя1 ~ • ;готоuнм бит 7 к уста,юпке ~меняем байт ст"1тусн 3.1.8. Написание процедуры ввода с клавиатуры общего назначения Система кодов, используемых клавиатурой, нс поддается простой интерпретации. Коды могут иметь длину 1 или 2 байта, и нет простого соответствия между длиной кода и тем, служит ли он для обозначения символа или для управления оборудованием. Не все комбинации клавиш выдают уникальный код, ПО:)томv необходимы добавочные усилия, чтобы различить их. Ни код~:t
128 1 Глава 3 ASCII, ни расширенные коды не упорядочены так, чтобы была возможна их простая группировка и проверка ошибок. Другими словами, процедура ввода с клавиатуры общего назначения требует хлопотливого программирования. Здесь приредены примеры на • Бейсике и показано использование прерывания 16Н, •а также как свести вместе большую часть информации, приведенной в данной главе. Общий алгорит:,,.1 пре;~:став.1:ен на рис. 3.3 . Получение символа Следующий символ Процедура анализа ,._ .. ., второго ~iiтa ,кода Процедура обра• ботки символов ASCII Процедура обра· ботки управп11ю­ щмх кодов ПроцеАура анализа Ctrl-H, Ctrl-l и Ctrl-M Рис. 3.3. Блок-схема процедуры вuода общего назначения Высокий уровень ,1 Процедура обработки ввода с клавиатуры, написанная на Бей1 сике, обеспечивает то же, что и ассемблерная процедура, за одн.Щ . -~
Клавиатура 129 исключением. Функция INKEY$ не предоставляет доступа к скан­ кодам. Это означает, что вы не можете сказать, получены ли коды ASCII 8, 9, 13 и 27 от нажатия клавиш <<BackSpace», <,ТаЬ», <<Enter)> и <<Escape)> или через Ctrl + Н, + 1, + М и + [. Различие может быть установлено проверкой бита статуса клавиши <<Ctrl)> по адресу 0040:0017 в момент нажатия клавиши. Но этот метод не будет работать, если введенный символ был запасен в буфере кла­ виатуры в течение некоторого времени. 100 С$= INKEY$:II' С$= .. . . Т/ШN 100 110 11' t,EN (С$) = 2 THEN 700 120 С= ASC(C$) 130 If' С<32 THEN 300 140 IF С<65 OR С>123 THEN 100 'r10.;1учениr симво.11а ес:1н расшнрс11111,1й, то 1111 700 ·шш•1е берем 1юмер кода ASCII ·сслн упраuляю1щ1й, то.на 300 г1ршшмаем только снмволы 150 '"пишущей машинки и делаем с ними, что хотим, например: 160S$=8$+С$ 170 PRINT С$; 180 ...... и т.д. 190 GOTO1IOO 'добавляем символ к строке ·выводнм его на экран ·11а 1111<щ следующею снм11ола 300 • ·процедура обработки управляющих кодон ЛSCII 310DEf'SEG =О указываем 11а ,~ачало памяти 320 REGISTER = РЕЕК<&Н417) 'берем регистр статуса •330 Х = REGISTER AND 4 340IFХ=ОTHEN500 ·х = 4, когда нажата Ctrl •если не нажата, то на 500 350 '"если это комбш~ация Сtrl-букна, то делаем, что хотим, 360 IF С= 8 THEN GOSUB 12000 1шnр~1мер, переходим на 370'"nроцедуру вывода экрана помощи ~• т.д. 380 GOTO 100 щ1 шюд следую1щ•1-о символа 500 '"nрЬцедура обработки 4 клавиш: декодирvст коды ASCII 8. 510 ... 9, 13 и 27, когда клавшuа «Ctr1»'. 1e наж.ата 520 IF С= 8 THEN GOSUB 5000 ·об:,,.боrка «BackSpace» 530 IF С= 9 THEN GOSUB 6000 ·ооработка «ТаЬ» 540 IF С= 13 THEN GOSUB 7000 'обработка «CR» 5 Р. джордеiiн
130 550 IF С= 27 THEN GOSUB 8000 560 GOTO 100 Глава З 'обработка «Esc,. на овод следующсl'О с•1м11(>ЛU 700 '"процедура обработки расширенных кодов 710 С$= RIGHT$(C$,l) 'берем только 2-й байт С$ 720 С= ASC(C$) ·переводим в ч~1словую форму 730 "'в С - расширен~ый ко• - делаем с ним, что хотим, например, 740 IF C<7l OR С>81 THEN 100 750 IF С= 72 THEN GOSUB 3500 760 ...... и т.д. 770 GOTO 100 Средний уровень 'берем только уnраме11ие курсором 'обработка "курсор-вверх" ·на ввод следующего символа Этот пример отличается от предыдущего методом распозна­ вания четырех частных случаев Ctrl + Н, + I; + М и + [. Здесь, когда встает вопрос о том, возник ли указанный код при н,tжатии одной клавиши или в комбинации с клавишей «Ctrl>>, проверяется скан-код. Этот метод более правилен, чем проверка бита статуса, так как скан-код запоминается в буфере клавиатуры, а установка бита статуса может быть изменена. ;---получение кода нажатой клавиши и определение его п111а NEXT: MOV АН.О ;функция вuода с кла1шатуры BIOS INT l6H ;получаем вuеде1111ый код СМР AL,0 ;проверка на расширенный код JE EXT_COD ;если да, то на специальную процедуру СМР AL,32 ;проверка на уnрамяющий символ JL C_COD ;если да, то на специальную процедуру СМР AL,65 ;если символ не входит о набор ш,шу- JL NEXT ;щей машинки, то берем сдедующ.,й СМР AL,123 JL NEXT ;---теперь обрабатываем симuол u AL STOSB ;з.•шоминаем симоол по адресу ES:Dl MOV АН.2 MOV DL.AL ;функщ1я вывода сим11ола 1ia экран ;помещаем символ в DL перед оыnодом
Клавиатура INT 2111 ;111,шодим ею ,ш экран JMP NEXT ;11ерсход11м к следующему с11м1юлу ;---анализируем упрап.,~яющ11с коды C_COD: СМР AL,13 ;код ASCII 13? JNE ТАВ ;сели 11ст, то следующая проверка Cl\{P АН,28 ;~1наче проверяем скан-код «CR• JNE С_М ;если 11ет. то было Ctrl + М CALL CARRIAGE_RET ;обр.1ботка воз~рата каретки JMP NEXT ;переход к с;1едующему симнолу С_М: CALL CTRL_M ;обр.1ботка Ctrl + М JMP NEXT ;переход к следующему с~1моолу ТАВ: СМР AL,9 ;проперка на табуляцию ... СМР AL,10 ;затем проверка других REJECT: JMP NEXT ;переход к следующему сим,юлу ;---анализ расширенных кодоо (2-й байт кода в АН): EXT_COD: СМР АН,71 ;проверка нижней границы JL REJECT СМР АН,81 JL REJECT ;если ме11ьше, то следующий символ ;прооерка 1~ерх11ей грш1ицы ;еСЛИ ООЛl,Ше, ТО С.Педующ~IЙ CИMIIOJI ;---АН содерж~IТ C~IMIIOЛ у11рш1J1е11ия •курсором, 111шш1з~1руем его СМР АН,72 ;"курсор-1111ерх"? JE C_U ;есщ1 1111, то на 11ро1tсдуру СМР АН,80 ;"курсор-11ш1з"? JE СD ;ес;ш да, то ш1 процедуру C_U: CALL CURS_UP ;вызов соотоетствующей процедуры JMP NEXT ;переход к следующему символу C_D: CALL CURS_DOWN ;оызов соотоетствующей про1~едуры JMP NEXT ;переход к следующему сим1юлу 3.1.9 . Перепроrраммнрованне прерывания кnавнатуры 131 • Когда микропроцессор клавиатуры помещает скан-код в порт А микросхемы 8255 (адрес порта бОН, см. [1.1 .1 ]), вызывается пре­ рывание 9. Задача этого прерывания - преобр.1зовать скан-код сим­ вола, основываясь на состоянии клавиш-переключат~ей, и помес­ s•
132 Глава 3 тить его в буфер клавиатуры., (Если скан-код соответствует кла­ вище-переключателю, то в буфер клавиатуры не пишется ничего, исключение составляет клавиша <<IПS>>, вместо этого прерывание изменяет· байты статуса, расположенные в области данных BIOS t3.l.7 ].) Прерывания "ввода с клавиатуры" DOS и ВIOS на самом деле - всего лишь прерывания "ввода из буфера клавиатуры". Они 1;1е распознают нажатия клавиш. Точнее, они читают интерпре­ тацию введенных клавиш, которую обеспечило прерывание 9. Заметим, что PCjr использует специальную процедуру (INT 48Н) для преобразования ввода от его 62 клавиш к 83-клавишному про­ токолу, используемому другими IВМ РС. Результат этой про­ цедуры передается прерыванию 9, которое выполняет свою работу как обычно. Прерыванием 49Н PCjr обеспечивает специальные' неклавишные скан-коды, которые потенциально могут устанавли­ ваться периферийными устройствами, использующими инфракрас- ную (беспроволочную) связь с клавиатурой. • Только при весьма необычном применении имеет смысл пере­ программирование этого прерывания, особенно учитывая, что MS- DOS позволя,ет перепрограммировать любую клавишу клавиатуры [3.2.6 ]. Если вам все же придется перепрограммировать преры­ вщ1-ие 9, эта глава даст вам.основы для старта. Сначала надо про­ читать [1.2.3 ], чтобы понять, как программируются прерывания. В прерывании клавиатуры можно выделить три основных шага: l. Прочитать скан-код и послать клавиатуре подтверждающий сигнал. 2·. Преобразовать скан-код в номер кода или в установку регис­ тра статуса клавиш-переключателей. 3. Поместить код клавиши в буфер клавиатуры. В момент вызова прерывания скан-код будет находиться в порте А. Поэтому сначала надо этот код прочитать и сохранить на стеке. Затем используется порт В (адрес 61Н), чтобы быстро пос­ лать· сигнал . подтверждения микропроцессору клавиатуры. Надо просто установить бит 7 в 1, а затем сразу вернуть его в О. Заме­ тим, что бит 6 порта В управляет сигналом часов клавиатуры. Он всегда должен быть установлен в l, иначе· клавиатура будет вв1ключена. Эти адреса портов применимы и к АТ, хотя он •и не имеет микросхемы интерфейса с периферией 8255. Сначала скан-код анализируется, была ли клавиша нажата (код нажатия) или отпущена (код освобождения). На всех маши­ нах, кроме АТ, код освобождения индицируется установкой бита 7 скан-кода в 1. Для АТ, у которого бит 7 всегда равен О, код осво­ божден~я состоит из двух байтов: сначала 0F0H, а затем скан-код. · Все коды освобождения отбрасываются, за исключением клавиш­ переключателей, для которых делаются соответствующие измене­ ния в байтах их статуса. С другой стороны, все коды нажатия
kлавиатура 133 обрабатываются. При этом снова могут изменяться байты статуса клавиш-переключателей. В случае же символьных кодов надо про­ верять байты статуса, чтобы определить, например, что скан-код 30 соответствует нижнему или верхнему регистру буквы А. После того как введенный символ идентифицирован, процедура ввода с клавиатуры должна найти соответствующий ему код ASCI! или расширенный код. Приведенный пример слишком короток, чтобы рассмотреть все ситуации . В общем случае скан - коды сопос­ тавляются с элементами таблицы данных, которая анализируется инструкцией XLAT. XLAT принимает в AL число от О до 255, а возвращает R AL !-байтовое значение из 256-байтовой таблицы, на которую указывает DS:BX. Таблица может находиться в сегменте данных. Если в AL находился скан-код 30, то туда будет помещен из таблицы байт номер 30 (31 байт, так как отсчет начинается с нуля). Этот байт в таблице должен б'ыть установлен равным 97, что дает код ASCII для "а" . Конечно, для получения заглавной А нужна другая таблица, к которой обращение будет происходить, еtли статус сдвига установлен. Или заглавные буквы люгут хра­ ниться в другой части той же таблицы, но в этом случае к скан- •.коду надо будет добавлять , смещение, определя~мсе статусом клавиш- переключателей. Наконец, номера кодов должны быть помещены а буфер клавиатуры. Процедура должна сначала проверить, имеется ли в буфере место для следующего символа. В [3.1 .1 ] показано, что этот буфер устроен как · циклическая очередь. Ячейка памяти 0040:00IA содержит указатель на голову буфера, а 0040:001С - указатель на его хвост . Эти славные указатели дают смещение в области •данных BIOS (которая начинается в сегменте 40Н) и находятся в диапазоне от 30 до 60. Новые символы вставляются в ячейки буфера с более старшими адресами, а когда достигнута верхняя граница, следующий символ переносится в нижний конец буфера. Когда буфер ·полон, указатель на его хвост на 2 меньше , указателя на голову, за исключением ситуации, когда указатель на голову равен 30 (начало области буфера), а в этом случае . буфер полон, когда указатель на его хвост равен 60. Для вставки символа в буфер надо поместить его в позицию, на которую указывает хвост буфера и затем увеличит,, указатель хвоста на 2; если указатель хвоста был равен 60, то надо изменить его значение на 30. Вот и все. Схема прерывания клавиатуры показана на рис. 3.4 . Низкий уровень Эффективная процедура требует глубокого продумывания. В этом примере представлены элементарные основы: принимаются ·
134 -Глава 3 только буквы нижнего и верхнего регистров, причем все они загру­ жены в одну таблицу, где буквы верхнего регистра находятся на 100 байт выше, чем их младшие братья. Анализируется только левая клавиша сдвига,_, и текущ~е состояние клавиши <<CapsLock» игнорируется. Получение скан-кода Анализ статуса клавиш Shifl и переключателей Выбор кодового номера из таблицы " Уста11овка байтов статуса BIOS Помещаем Ои Да расширенный код в 1--------t буфер клавиатуры Помещаем в буфер код • ASCllи скан-код Рис. 3.4. Прерывание клаnш1туры ;---n сегменте данных TABLEDB 16 DUP(O) DB DB DB DB ,qwertyuiop· ,0 ,0,0 ,0 ·a sdfghjkl' ,0 ,0 ,0 ,0 ,0 ·zxcvbпm· 16 DUP(O) ;пропускаем первые 16 байт ;верхний ряд кла~шатуры ;средний ряд кла11~~атур1>1 ;нижний р11д клш~иатур1,1 ;нронуск до 11срх11ст рсп,стра
Клав,μатура DB DB DB 'QWERTYUIOP' ,0,0,0,0 ;те же симооды 1ш верхнем 'ASDFGHJКL' ,0,0,0,0,0 ;регистре 'Z XCVBNM' ;---в начале программы уста~швливаем 11рерь111н1н1е CU • :запрет 11рерь111аш1й PUSH DS ;сохраняем регистр MOV AX,SEG N_KEY ;DS:DX должны указыВ!lть на MOV • DS,AX ;процедуру обработки MOV DX,OFFSET N_KEY ;прерывания MOV AL,9 ;номер вектора nрерыв.'11шя MOV АН,25Н ;номер функции DOS INT' 2IH ;меняем вектор прер1,1В11ш1я РОР DS ;восстанавлив.'lем регистр SТI ;разрешаем nрерыв.'111ия Программа продолжается, оставаясь резидентной [ 1.3 .4 ]. ;---это само прерывание кдаuиатуры N_KEY PROC FAR PUSH АХ PUSH nx PUSH СХ PUSHDI Pi.JSH ES ;сохраняем псе изме11яем1,1е ;ре1·истр1,1 ;---получаем скан-код и посылаем сш·1~ал 11одтuерждеш1и IN AL,601-1 ;полу1Jаем скан-код из ~юрта А MOV ЛН,АL PUSH АХ IN АL,61Н OR AL,100000008 ;помещаем кошно 11 ЛI 1 ;сохраняем скан-код ;•штаем состо11ш1е ,юрта П ;устанавлиuаем бит 7 OUT 6IH,AL ;110сылаем ~1з!"е11енный байт 11 110рт AND AL,01"1111118 ;сбрасьшаем бит 7 OUT 61H,AL ;1юз11ращаем состоs1ш1е ,юрта В ;---ES должен указывать 11:1 область да1111ых BIOS MOV ЛХ,40Н MOV ES,AX РОР АХ ;---проверка кла11иши сдвш-а СМР AL,42 JNE KEY_UP MOV BL,l ;устаIIаI1JIиваем сегмсII1· ;возвращаем скан-код из стека ;1шжат левый сдв~,11·'! ;нет - смотрим следующее ;да - измением бит статуса 135
136 OR JMP ES:[l7Н),BL QUIT ;меняем прямо реr~1стр статуеа ;выход из процедуры Глава З КЕУ UP:CMP AL,170 ;левый сдвиr отпущен? JNE MOV AND JMP NEXTKEY 8L,llll11108 ES: [17Н] ,81, QUIT ;нет - смотрим СJ1едующее ;да - меняем бит статус11 ;меняем прямо реr~1стр статуса ;выход ~13 процедуры NEXTKEY: ;просмотр других nереклю•штслс~1 ;---это символьная клавиша - интерпретируем скан-код TEST AL, l 00000008 JNZ QUIT MOV BL,ES:[l7Н] TEST BL,00000011 В JZ CONV ADD AL,100 ;код освобождения клавиши? ;да - выходим из процедуры ;иначе,берем байт статуса ;клавиша сдвига 1шжата? ;нет - уходим дальше ;да - зна•шт, заглавная буква CONV: MOV BX,OFFSET TABLE ;готовим табJ1ицу XLAT TABLE ;преобразуем ска11-код в ASCII СМР AL,0 ;возвращен О? JE QUIТ ;если да, то iш выход ;---код клавиши готов, проверяем, не 'llOJIOH JIИ буфер клавиатуры моv моv MOV СМР JE INC INC СМР JE JMP BX,lAH CX,ES:[BX] DI,ES: [ВХ] + 2 СХ,60 H_END сх сх CX,Dl . QUIT GO_AH ;смещен~1е указателя на голову ;по.11уч~ем его з11ачение ;получаем указатель на хвост ;голова на вершине буфера? ·;да - переходим к специальному случаю ;увеличиваем указатель на голову ;на 2 ;сравшшаем с указатеJ1ем на х11ОСт ;если р111111ы, то буфер _11оло11 ;~111а•1е,вста~1дяем сим1юл Н END: СМР Dl,30 ;проверка сnец~~ал1,1ю1u СJ1у•111я JE QUIТ ;есш1 буфер nоло11, то 11ыход ;---буфер не полон - вставляем в него символ GO_AH: MOV ES: [DI] ,AL ;помещаем симвоJ1 в позицию хвоста СМР Dl,60 ;хвост в ко11це буфера? JNE NO_W ;если нет, то добаw1яем 2 MOV DI,28 ;иначе,указатель хвоста = 28 + 2 NO_W: ADD DI,2 ;по.nучаем новое значе11ие хвоста MOV ES: [ВХ] + 2,DI ;nосыщ1ем его в область да1111ых
Клавиатура ;---завершеш1е прерывания QUIТ: РОР ES РОР DI РОР СХ РОР ВХ РОР АХ MOV AL,201-l OUT 20H,AL ;восстанавливаем изменяемы.: ;регистры ;выдаем сипшл об окон•шнии ;аппаратного nрерываш1я IRET ;возврат из прерыван~1я N_KEY ENDP 137 3.2 . Доступ к от дельным клавишам Процедура обработки нажатия клавиши должна проверять массу различных типов клавиш и условий, поскольку как одно-, так и двухбайтовые коды моrvт появляться в комбинации с клавишами-переключателями. не· все клавиши лоmчески сгруппированы по типу кода, который им соответствует. Например, клавиша «Backspace,> генерирует однобайтовый код ASCII, а ·клавиша <<Delete,> - двухбайтовый расширенный код. Кла­ виша <<Ctrl» генерирует однобайтовый код в сочетании с алфавит­ ными клавишами и двух(5айтовый код в остальных случаях. Эти нерегулярности возникаЮ'Г из-за ограниченности набора ASCII: прерывание клавиатуры следует соглашениям ASCII, когда это возможно, но когда невозможно - выдает свои (расширенные) коды. В данном разделе перf'числены различные группы клавиш, даны их коды и указаны встречающиеся аномалии. В большинстве случаев эта информация доступна в менее удобном виде из таблиц кодов ASCII и Р\IСШиренных кодов, приведенных в разделе 3 этой главы. Здесь обсуждаются также специальные свойства, припи­ сываемые отдельным клавишам Бейсиком, а также специальная обработка для интерпретации отдельных клавиш (таких, как «Backspace>>), применяемая в прерываниях DOS. 3.1.1. Испопьзованне клавнш ccBackspace)), ((Enter)), ccEscape)) н «ТаЬ)) Клавиши <<Backspace,>, «Enter», «Escape» и <<ТаЬ>> - единствен­ ные четыре несимвольные клавиши, генерирующие однобайтовые
138 Глава 3 коды ASCII. Эти коды содержатся в наборе управляющих кодов [7.1 . 9 ], которые занимают первые 32 кода в наборе ASCII. Эти четыре кода могут быть получены та1<же комбинацией буквенных клавиш с клавишей «Ctrl»: ASCII 8 Backspa.:c C1rJ ~ 11 ASCII 9 ТаЬ Ctrl+I ASCII 13 Enter Ctrl+м ASCII 27 Escape Ctrl+[ В [3.2.2] показано, как различать нажатие одной клавиши и комбинацию с клавишей <<Ctrl>>. Отметим, что обратная табуля­ ция, производимая· при комбинации клавиш «Shift>> + «ТаЬ>>, вы­ дает расширенный код О; 15. Некоторые из прерываний обработки ввода с клавиатуры авто­ матически интерпретируют эти четыре специальных кода. В Бей­ сике функция INPUT реагирует на «Backspace>>, «ТаЬ>> и «Enter>>. Функция IN_KEY$ не интерпретирует ни один из управляющих кодов, поскольку у нее нет автоматического эха на экран. Всю работу должна выполнять ваша программа. Напомним, •что для управления движением курсора Бейсик предоставляет функцию ТАВ. Прерывания BIOS и DOS, которые выдают эхо на терминал, интерпретируют также клавиши <<Backspace>> и <<ТаЬ>>. После того как эти коды интерпретируются ~оответствующим образом, коды ASCII все равно появляются в AL, где они могут быть включены в строку символов или игнорированы в зависимости от того, что требуется. 3.1 .1. Нспоnьзованне кnавнш-перекnючатеnей <<Shift1,, «Ctrl11 н «Alt,1 Три типа клавиш-переключателей заставляют другие клавиши клавиатуры генерировать различные коды. Как правило, такие комбинации генерируют расширенные коды. Но в двух случаях они дают коды ASCII: (1) когда используется ,клавиша «Shift» с клавишами алфавитно-цифровых символов и (2) нажатие клавиш в комбинации от Ctrl-A до Ctгl-Z дает ASCII коды от 1 до 26. Все остальные комбинации позволяют получить расширенные коды, перечисленные в [3.3 .5 ]. PCjr имеет несколько исключений, кото-' рые обсуждаются ниже. Недопустимые комбинации клавиш не производят кода вообще. За исключением специальных комбинаций с <,Ctrl-Alt>>, одновре­ менное нажатие нескольких переключателей приводит к тому, что
Клавиатура 139 только один из них становится эффективным, причем приоритет у <<Alt», затем <<Ctrl>> и затем <<Shift>>. В [3 ..1.7] показано, как прове­ рить, нажата ли в данный момент клавиша-переключатель, а в [З.2.3) - как использовать клавишу «ScrollLock» в качестве пере­ ключателя с любой друrой клавишей клавиатуры. Другие комби­ нации с клавишами-переключателями можно сделать допусти­ мыми, только полностью переписав прерывание клавиатуры, которое заменило'бы прерывание BIOS [3.1.9). Существует проблема, связанная с некоторыми комбинациями с клавишей <<Ctrl>>, такими, как Ctrl + Н, 1, М и [, поскольку они генерируют коды ASCII, идентичные тем, которые генерируют клавиши «Backspace>>, <<ТаЬ,>, <<Enter,> и «Escape,>. В [3.1.8] пока­ зано, как программа на ассемблере может, проверив скан-коды, определить, была ли нажата управляющая клавиша или комби­ нация алфавитно-цифровой клавиши с <<Ctrl,> (скан-код находится в АН, когда мы получаем код нажатой клавиши через прерывание 16Н). К сожалению, программы на Бейсике лишены такой воз­ можности. В подобном случае программа_ может попытаться раз­ личить эти две возможности, анализируя состояние регистра ста­ туса. Если бит 2 байта статуса по адресу 0040:0017 установлен, то клавиша <<Ctrl,> нажата. Этот метод работает только в тот момент, когда происходит нажатие клавиши, но не тогда, когда вы берете символ из буфера клавиатуры через некоторое время. Клавиатура PCjr имеет только 63 клавиши по сравнению с 83 для IВМ РС или ХТ и 84 для АТ. Некоторые комбинации кJ!'авиш­ переключателей служат для имитации недостающих клавиш (ком­ бинации с использованием функциональных клавиш приведены в [3.2 .5 ]): Комби1ш.!Шя клавиш PCjr Alt+l'n+0-9 Alt+/ Alt + Alt+[ Alt+1 Alt + Shift + Del Эквиваленты в РС/ХТ/АТ' О-9(скан-кодыдоnолнительной цифровой клавиатуры) \ * (скан-код, как от клавиши PrtSc) . (скан-код, как от дополни­ тельной клавиатуры) Клавиатура , PCjr допускает также следующие уникальные комбинации с участием клавиш-переключателей:
140 Fn+Shift+Esc Ctrl + Alt + CapsLock Ctrl+Alt+Ins Ctrl + Alt + CursorLeft Ctrl + Alt + CursorRight Глава 3 переклю•шет цифровые клавиши в функциональные переключает звуковое подтвержде11ие нажатия клавиши запускает диагностику сдвигает экран влево сдвигает экран вправо 3.1 .3 . Использование клавнш-переключат~лей: ccHumLock11, ccCapsLockJJ, cclnSJJ н ccScrollLockJ1 За исключением клавиши <<lns>>, все остальные клавиши-пере­ клю9атели не про·изводят кода, который помещался бы в буфер клавиатуры. Вместо этого они изменяют состояние двух байтов статуса, которые расположены в области данных BIOS по адресам 0040:0017 и 0040:0018. Прерывание клавиатуры проверяет установку этих байтов перед тем, как присвоить код введенному символу. Ваши программы имеют доступ к регистрам статуса и могут изменить установку любой из клавиш-переключателей; как объяснено в [3.1.7 ]. Другие биты регистра статуса показывают, нажата ли данная клавиша-переключатель в текущий момент. Это свойство позво­ ляет программе использовать клавиши-переключатели в качестве клавиш сдвига. Возможны потенциальнь~е применения этого, пока не создано новых кодов клавиш. Например, <<Scrol\Lock>> может быть использована для того 7 чтобы добавить добавочный набор комбинаций сдвиг + функциональная клавиатура. Программа, которая будет получать код обычной функционал1,ной клавиши, должна проверять, нажата ли клавиша <<ScrollLock», и соответст­ венно интерпретировать нажатие клавиши. Отметим, что любая из клавиш «Shift>> обращает текущую установку клавиши «NumLock». Клавиша «Ins» .помещает в буфер клавиатуры код 0;82, кото­ рый ваша программа может прочитать в любой момент. Ощ-1ако установка для «Ins» в байтах регистра статуса меняется немед­ ленно. Даже если в буфере нет места для кода «Ins>>, в регистре статуса при нажатии клавиши вносятся изменею1я. Как «lns>>, так и <<Scrol\Lock» не влияют на другие клавиши клавиатуры (в отли­ чие от <<Nu-ml.ock>> и <<CapsLock»). Вы можете приписать им любую роль. Техническое руководство IВМ утверждает, что клавиша «Scrolll.ock>> должна использ6ваться для переключения между сос- . тояниями, когда нажатие клавиши перемещения курсора приводит к сдви~кlе экрана, а не к передвижению курсора.
Клавиатура 141 Конечно, вы можете создать все нужные для вашей программы I<Лавиши-переключатели, просто назначив клавиши для этой цели. Хотя у вас нет готовых регистров статуса,, но вы можете создать переменную, значение которой -1 соответствует включенному сос- , тоянию вашего переключателя, а значение О - выключенному. Например, используем клавишу FlO для включения и выключения переменной Clock: 100 '"переключение статуса переменной 110 CLOCK = -1 начинаем с вклю•1енным состоянием 120 IF Х« = 100 THEN NOT CLOCK переключаем переменную 3.2 .4. Нспоnьзованне цнфровон допоnннтеnьнон кnавнатуры н кnавнш перемещення курсора . Для IВМ РС и ХТ дополнительная цифровая клавиатура вклю- чает цифровые клавиши, клавиши <<lns» и «Del,>, а также клавиши « + >> и <~ -> >. На АТ добавляется клавиша «Sys Rec>>, в то время как PCjr имеет только 4 клавиши перемещения курсорц (остальные могут быть _эмулированы специальными «омбинациями с клави­ шами <<Shift>> и <<Fn>>, описанными в [3.2 .21 и [3.2 .5]). Клавиша «NumLock>> переключает дополнительную клавиатуру между 1циф­ рами й клавишами управления курсором. К\Jlавиши <<lns» и <<Del>> работают, только если режим <<NumLock» включен, т.с. дополни­ тельная клавиатура выдает 'цифры. Клавиши << + >> и <<-» выдают одни и те же кс;щы независимо от установки режима «NumLock>>. Цифровые клавиши дополнительной клавиатуры выдают в точ­ ности те же однобайтовые коды, что и цифровые клавиши верх­ него ряда основной клавиатуры, т.е. коды ASCII от 48 до 57 для цифр от О до 9. Это верно и для клавиш << + » и <<->>. Программисты на ассемблере могут определить, какая из двух клавиш нажата по скан-коду клавиши, который находится в АН при возврате как из прерывания lбН, так и из процедур ввода одной клавиши преры­ вания 21Н. Отметим, что любая из клавиш <<Shift» переводит кла­ виши дополнительной клавиатуры в режим, противоположный тому, который установлен клавишей «NurriLock>>. Установка кла­ виши «CapsLock>> не имеет значения. -Клавиша "5" в центре активна только как цифровая клавиша и в· режиме перемещения курсора вообще не выдает кода. Кроме четырех общепринятых стрелок, клавиши управления курсором включают также ·«Home>>, «End», «PgUp» и «PgDn», которые часто используются для перемещения курсора сразу на целую строку или страницу. Все они генерируют двухбайтовые расширенные коды. Эти клавиши не обеспечивают прямого конт-
142 Глава 3 роля над курсором. Они просто выдают коды, как и все другие клавиши, и задачей программиста становится преобразование этих кодов в перемещения курсора на экр<!не. Допустимы некоторые комбинации клавиш дополнительной клавиатуры с клавишей <<Ctrl,>. <<NumLock>> должна соответствовать • режиму управления курсором, чтобы эти комбинации работали. В (3.1.7] показано, как ваша программа может автоматически уста­ навливать режим NumLock. Вот перечень кодов клавиш дополнительной клавиатуры: Коды ASCII: 43 45 46 48-57 Расширенные коды: 72,75,77,80 71,73,79,81 82,83 ll5,ll6 l l7,l l8,l l9,l32 + 0-9 C11rsorUp,J,eft,Right & Down Home,PgUp,End,PgDn lns,Del Ctlr-cнrsor lcft, -cнrsor right Ctlr-end, -PgDn, -Home, - PgUp АТ имеет 84-ю клавишу, <<Sys Req,>, которая уникал1,на по своей функции. _Клавиша предназначена для многопою,зователь­ ских систем как способ входа в главное меню системы. Когда кла­ виша нажимается, в АХ появляется код 8500Н и выполняется пре­ рывание lSH. При отпускании клавиши в АХ появляется код 8501Н и опять же выполняется прерывание l5H. BIOS АТ не обра­ батывает функции 84Н и 85Н прерывания l5H, а просто д-елает возврат. Но можно программно заменить вектор прерывания для lSH, чтобы он указывал на процедуру обработки клавиши «Sys Reqp. Такая процедура должна сначала прочитать AL, чтобы узнать, была ли клавиша нажата (AL = О) или отпущена (AL = 1). Заметим, что прерывание lSH предоставляет ряд процедур, ·и некоторые из них могут потребоваться программе обработки «Sys Req,>. В этом случае процедура обработки <<Sys Req» должна восстанавливать замененный ею вектор прерывания, и если в АН указаны функции, отличные от 84Н и 85Н, то надо передап, у~равление оригинальному прерыванию 15Н LI .2 .4 ].
Клавиатура 143 3.1 .S. Нсnо11ьзование функциональных к11авиw 1О функциональных клавиш генерируют различные коды в сочетании с «Shift», «Ctrl» и «Alt», что обеспечивает 40 разных вариантов. Во всех случаях генерируется дву.х;байтовый расши­ ренный код, в котором первый байт всегда ASCII О, а второй байт приведен в таблице: Коды 59-68 84-93 94-103 104-113 Клавиши FI-FI0 Shift + Fl'-Fl0 Ctrl + Fl-FI0 Alt + FI-FI0 Слишком много комбинаций с использованием функциональ­ ных клавиш могут смущать пользователя, но если вам потребова­ лось еще 1О комбинаций, то можно применять сочетание «ScroЦl.ock» + «Fn», как объяснено в [3.2 .3 ]. Клавиатура PCjr имеет только 62 клавиши по сравнению с 83 для IВМ РС и ХТ и 84 для АТ. Некоторые комбинации с функци­ ональными клавишами эмулируют часть недостающих клавиш сог- • ласно следующей таблице: PCjr комбинации Fn+1-0 Fn+В Fn+Е Fn+Р Fn+Q Fn+S Fn + CursorLeft Fn + CursorRight Fn + CursorUp Fn + Ct1rsorDown Fn+- Fn+ Эквиваленты в РС/ХТ/АТ FI-FI0 Break Ctrl + PrtSc Shift + PrtSc Ctrl + Numl.ock Scrolll.ock PgUp PgDn Home End (скан-код серого минуса) (скан-код серого плюса) Комбинации с клавишами~переклК!ча;елями описаны в [3.2 .2 ].
144 Глава 3 3.2.6. Перепроrраммнрованне отдельных клавнw Под перепрограммированием клавиши понимается способ заставить ее выдавать другой код. Но к тому времени, когда прог­ рамма получает код нажатой клавиши, прерывание клавиатуры уже проинтерпретировало входящий скан-код и преобразовало его в некоторый заранее предопределенный код ASCII или расши­ ренный код. К счастью, начиная с MS-DOS версии 2.0, система содержит средства перепрограммирования клавиш. Это средство действует, только если ввод воспринимается через функJ\ИИ DOS ввода с клавиатуры, функции прерывания BIOS 16Н продолжают интерпретировать нажатия клавиш нормальным образом. Перепрограммирование доступно за счет Еsс-последователь­ ностей. Короткая строка, которая начинается с символа Esc (АSСП 27), предназначается для вывода на "стандарт1ное устройство вывода", т.е. на терминал. Но благодаря наличию кода Esc, сим­ волы даже не достигают монитора. Вместо этого такая строка заставляет MS-DOS по-другому интерпретировать клавишу, ука­ зан;tую в этой строке. Каждое изменение клавиши требует собст­ вен 11ой строки, при этом оди~ и тот же код может присваиваться какому угодно количеству клавиш. Общий вид такой строки следующий: она начинается с кода Esc (ASCII 27), за которым идет [, затем номер кода переопреде­ ляемой клавиши, затем точка с запятой (;), затем новый номер кода, присваиваемый клавише, и, наконец, символ· р. Таким образом, строка 27,' [65;97р' меняет А (ASCII 65) на а (ASCII 97). Расширенные коды записываются с указанием обоих байтов, при­ чем за первым нулевым байтом должна стоять точка с запятой. Строка 27,' [0;68;0;8Зр' присваивает клавише Fl0 (0;68) тот же код, что и клавише Delete (0;83). Вы можете присваивать только расширенные коды, приведенные в таблице расширенных кодов [3.3 .5 ]. Существует несколько варианто11 допустимого вида строки. Во­ перю-,1х, символьные к.павиши могvт обозначаться самим символом, заключенным в кавычки. Таким образом, строка 27,' ["А";''а"р' также меняет А на а. Во-вторых, клавише может быть присвоена целая строка символов путем указания символов или их кодовых номеров в выражении. Строка 27,'["А";"А is for Apple"p' приведет к тому, что при нажатии на клавишу А в верхнем регистре будет псч<.iтаться вся строчка А is for Apple. На самом деле эти Еsс­ посдсдовательности - ничего более, чем строки, в которых первый код и.'Iи символ указывает, какую клавишу нужно переопределить, а оставшаяся часть строки указывает, какое значение вы хотите ей при:щтr,. Помните, что номера кодов должны быть всегда разде­ лены точкой с запятой, а символы заключены в кавычки. Коды и
\ клавиатура 145 символы могут быть перемешаны в любых сочетаниях. Для тогd чтобы такие переопределения клавиш стали возможны, необхо­ димо, чтобы драйвер ANSI.SYS был загружен при заrрузке опера­ ционной системы. В противном случае Еsс-последовательности бу­ дут иrнорироваться. В приложении Д показано, как это делается. Некоторь1е аспекты функционирования клавиатуры проrрамми­ руются на PCjr и АТ. Процедуры, доступны<; для АТ, интересны в основном для системных программистов; поскольку они нужны весьма немногим и достаточно сложны, мы не будем рассматривать их здесь. При необходимости вам придется обратиться к Техничес­ кому руководству по АТ. В случае PCjr прерывание BIOS 16Н имеет две дополнительные функции (АН = 3 и АН = 4)., первая из которых устанавливает частоту автоповтора. "Частота автопов­ тора" - это та частота, с которой клавиша посылает свой код, когда она постоянно держится нажатой. Вторая функция включает и выключает звуковое подтверждение нажатия клавиши. Для фун­ кции 3 надо поместить в AL О, чтобы вернуться к частоте автопой­ тора, устанавливаемой по умолчанию, 1, чтобы увеличить началь~ ную задержку перед тем, как начинается режим автоповтора, 2, чтобы уменьшить частоту автоповтора вдвое, 3, чтобы установить свойства 1 и 2 вместе, и 4, чтобы выключить автоповтор вообще. Для функции 4, поместив в AL l, вы будете иметь звуковое под­ тверждение нажатия клавиши, а О - выключите ero. Высокий ур~вень К несчастью, операторы Бейсика PRINT и WRITE не работают с Еsс-посл'едовательностямиt Программы на Бейсике должны вклю­ чать простые ассемблерные подпрограммы, использующие преры­ вания вывода MS-DOS, обсуждаемые ниже. В приложении Г пока­ зано, как включить ассемблерные процедуры в программы на Бей­ сике" В приведенном примере предполагается, что процедура .нахо­ дится в памяти,· начиная с ,адреса 2000:0000. Операторы DA ТА содержат ассемблерный код. В конце строки переопределения кла­ виши должен быть добавлен код $. 100 DЛТА &Н55, &Н8В, &НЕС, &J-18B, &HSE , &НОб, &Н8В, &Н57 110 DATA &H0l, &НВ4, &НО9, &HCD, &Н21, &HSD, &НСА, &_1l02 , &НОО 120 ·помещаем процедуру в память по адресу 2000:0000 130 DEF SEG = &Н2000 ·опреде;1яем сегмент 140FORN=0ТО 16 150 READ Q 160 РОКЕ N,Q ·процедура длшюй 17 байт ·•,~паем байт ·помещаем его u .~амять
146 170 NEXT 180 ... меняем А на а 190 QS = CHRS<27) + "[65;97pS" 200 ROUTINE = О 210 CALL ROUТINE(Q$) Средний уровень •строка 11ереоr1рс:1с;1сш111 указываем 1111 строку uызьшаем нроце;~уру Глава 3 Используйте функцию 9 прерывания 21 Н для посылки строки на стан;~артное устройство вывода. DS:DX должны указывать на первый символ строки в памяти и строка должна завершаться сим­ волом ,$ <24Н). Здесь F2 (0;60) переопределяется таким образом, чтобы она действовала как Del (0;83). ;---в сегменте да11ных СН_КЕУ DB 27,'[0;60;0;83р$' ;---для изменения оnределеш1я клавиш~• LEA DX,CH_KEY MOV АН,9 INT 21Н ;DS:DX должны указывал, на строку ;номер фу11кцш1 ;переопреде;1е11не кш111н1ш1 3.1.7. Создание макроопредеnеннй дnя отдеnьн1>1х кnавнw Макроопределение - это строка символов, которая будет выво­ диться при нажатии одной клавиши. Макроопределения могут быть запрограммированы в интерпретаторе Бейсика или на уровне операционной системы для уменьшения печатания. Поскольку строка может содержать управляющие коды, такие, как символ возврата каретки (ASCII . 13), одно макроопределение может выполнять целый набор команд. Для ускорения разработки прог­ рамм, например, можно написать макроопределение, содержащее все необходимые командь1, чтобы оттранслировать и скомпоновать определенную программу. Макроопределения, обеспечиваемые Бейсиком, работают как в бейсиковских программах, так и на командном уровне Бейсика. Например, если вы запрограммировали клавишу, чтобы при ее нажатии выводилось слово "Орангутан", то при нажатии этой кла­ виши функция INPUT получит всю эту строку, а цикл, включа­ ющий INKEY$, последовательно получит девять символов. С дру­ гой стороны, макроопределения, созданные на уровне операцион­ ной системы, всегда работают на командном уровне DOS, но внутри программ они будут работать, только если программа для /
клавиатура 147 ввода с клавиатуры использует функции DOS. Поскольку боль­ шинство коммерческих программных продуктов используют прерывание BIOS 16Н, для этих программ макроопределения не будут работать. Конечно, средства для создания макроопределений могут быть вставлены в процедуры ввода с клавиатуры. Например, чтобы позволить пользователю программы создать макроопределение .для Fl, запросив строку и поместив ее в MACR01$, надо ~а Бейсике написать что-то вроде i ООО ·--процедура ввода расширенного кода, в С - 2-й байт _кода 1010 IF С= 59 THEN LOCATE Х,У: PRINT MACRO1$ Высокий уровень Бейсик имеет встроенный механизм создания макроопреде­ лений, но он позволяет программировать только .1 О функциональ­ ных клавиш, а строки должны быть не длиннее 15 символов. Бей­ сик рассматривает функциональные. клавиши как программиру­ емые клавиши. Оператор КЕУ присваивает макроопределение дан­ ной клавише. Строка КЕУ 5, "END" приводит к тому, что функци­ ональная клавиша #5 будет посылать слово· END в текущую позицию курсора на экране. Символы, составляющие строку, могут вводиться как строки символов, как коды АSСП (с использованием CHR$) или как ком­ бинация того и другого. Команды КЕУ 5, "А" и КЕУ 5,CHR$(6S) эквивалентны. Для того чтобы строка сразу исполнялась, надо добавить в конце строки символ возврата каретки (ASCII 13). Команда FILES, выводящая каталог диска, ИСi:полняется после того, как вы присвоите это значение Fl командой KEYl, "FП"ES" + CHR$(l3). Бейсик присваивает десяти функциональным клавишам рас­ пространенные операторы Бейсика. Вы можете отменить макрооп­ ределение для данной клавиши, присвоив ей пустую строку, например, команда КЕУ l , "" приведет к тому, что при нажатии Fl ничего вводиться не будет. Первые шесть символов каждой строки автоматически выводятся· в нижней части экрана интерпре­ татором Бейсика. Вы можете управлять наличием этого вывода, используя кома,щы КЕУ ON и КЕУ OFF. Для того чтобы вывести на экран полные определения клавиш, введите команду КЕУ LIST. Вот несколько примеров: КЕУ 1, "ERASE" КЕУ 10,"LIST" + СНR$(13) КЕУ 7,"" ; теперь Fl выводит "ERASE" ; теперь Fl О выдает листинг ; теперь F7 ничего не выдает
148 КЕУ OFF КЕУ ON КЕУ UST , Глава 3· ; подавляет вывод вшtзу экрана ; вклю•rает вывод вш1зу экрана ; выдает сш,сок з1ш•~еш1й 10 клшшш Для создания макроопределений других клавиш в Бейсике, вы должны использовать средства MS-DOS, описанные в [3.2 .6 ]. Средний уровень В MS-DOS макроопределения создаются с помощью метода перепрограммирования клавиш, описанного в [3.2 .6 ]. Единст­ венное отличие в том, что клавише сопоставляется целая строка символов. Строка может быть введена в виде символов, заключен­ ных в кавычки, а также в виде кодов или комбинации того и дру­ гого. Вот несколько примеров: 27," [ "A ";"SET"p' 27,T'ASET"p' 27," [27;"dir";l3p' 27,"[0;59;"сору *.* b:";J3p· 27,' [0;68;0;72;0;72;0;72р' ;присва11вает SET за1·лавной А ;эквш~.1лент1ю 11рсдыдущему ;приС11аио.1ет dir«CR» к;шв11ше Esc ;присваивает FI команду ;заставляет FIO сдв1шуть курсор на ;тр11 строки вверх 3.1 .8. Создание процедурь1 о~работкн ccCtrl-Break)) 11 Когда вводится комбинация «Ctrl-Break>>, прерывание клавиа­ туры устанавливает флаг, указывающий, что должна б1>1т1, выпол­ нена процедура обработки «Ctrl-Break•. Управление передается этой процедуре только в тот момент, когда программа обращается к функции DOS, способной распознавать этот флаг. Обычно только стандартные функции ввода/вывода. MS-DOS могут_ распознавать этот флаг (функции от I до ОС прерывания 21 Н, за исключением функций 6 и 7). Но поместив строку,-ВRЕАК = ON либо в файл AUTOEXEC.BAT, либо в CONFIG.SYS, используемые MS-DOS, при старте системы, вы пол-учите ситуацию, когда обращение к любой функции DOS приведет к вызову процедуры обработки «Ctrl-Break>>. При э1:ом выполнение программы будет немного замедлено. ' • . Процедура обработки «Ctrl-Break• дает возможность завершить· программу в любой момент времени. Когда функция DOS распоз­ нает статус «Ctrl-Break•, управление передается процедуре, на которую указывает вектор прерывания 23Н. DOS применяет эту процедуру для завершения работающей программы. Но процедура
Клавиатура 149 может быть переписана вами, с тем чтобы она удовлетворяла любым вашим требованиям. Эта процедура должна быть програм­ мируемой, чтобы перед завершением программы могли быть вы­ полнены все критические операции. Может потребоваться вырав­ нивание стека, для того чтобы •SP указывал на второе слово от вершины (первое слово для программ СОМ) перед выполнением завершающей инструкции RET. Векторы прерывания, измененные программой, должны быть восстановлены, а все открытые уст­ ройства ввода/вывода - закрыты. Если были запрещены прерыва­ ния, то надо разрешить их. Все это должно обеспечить машине возможность нормально работать со следующей программой после завершения программы по <<Ctrl-Break». Альтернатива - сделать процедуру обработки «Ctrl-Break», состоящей из одной инструкции IRET, что запрещает завершение программы таким способом. Средний уровень В данном. примере выход из программы происходит после выравнивания стека. Процедура кончается инструкцией RET, а не IRET, поскольку в данном случае она действует точно так же, как и инструкция RET при норt.,Jальном завершении программы. В момент, когда .она используется, указател1, стека (SP) должен ука­ зывать на второе слово стека. Это предполагает, что программа имеет форму ЕХЕ. Помните, что стек помещает свое первое слово в самую старшую ячейку памяти, второе - в ячейку ниже и т.д. Если размер стека 400 байт, то надо установить SP на 396. Для программ СОМ надо устанавливать указатсл1, стека на первое слово стека или просто завершать процедуру . обработки «Ctrl-Break» прерыванием 20Н. ;---это новая процедура обработки «Ctrl-Break» ' СВPROCFAR - MOV АХ)96 MOV SP,AX ;з1~ачеш1е для 1пороrо сло11а стека~ ;выра111ш11асм указатель сrска ~ RET ;11оз11раr 11 DOS СВENDP ;---~1зменение вектора прер1,1щ111~111 PUSH DS ;сохрат1см рсп,стр MOV AX,SEG С_В ;1хпо11нм адрес 11роцсдур1,1 MOV DS,AX MOV DX,OFFSET с_в MOV AH,2SH ;11омер функции MOV АL,2ЗН ;номер вектора INT 21Н ;изменяем вектор РОР DS ;восста11авшшаем реr~1стр
150 Глава 3 Программа может в любое время проверить, рыл ли сделан запрос на выполнение процедуры обработки «Ctrl-Break>>. Надо поместить в AL О и вызвать функцию 33 прерывания 21Н. При возврате DL будет содержать 1, если был установлен флаг преры­ вания по <<Ctrl-Break>>, и О - в противном случае. Если при вызове поместить в AL 1, то статус будет установлен. В этом случае перед вызовом функции поместите в DL О или 1, чтобы флаг был уста­ новлен или очищен. ( 3.1 .9 . Перепроrраммнрованне клавншн ccPrtSc)J Клавиша «PrtSC>> выдает звездочку (ASCII 42), если нажать ее одну, и расширенный код 114, если нажать ее вместе с клавишей «Ctrl,>. Но комбинация «Shift>> + <<PrtSc» имеет совершенно от­ дельный статус. Нажатие на другие клавиши заставляеr преры­ вание клавиатуры помещать их коды ·в буфер клавиатуры (или, для клавиш-переключателей, записывать их состояние [3.1. 7 ]) . Нажатие клавиши не влияет на выполняемую программу, до тех пор пока программа не станет считывать символ клавиши из буфера клавиатуры. Но комбинация <<Shift>> + <<PrtSC>> заставляет прерывание клавиатуры немедленно передать управление про­ цедуре, на которую указывает вектор прерывания 5. В некотором смысле она работает как аппаратное прерывание. Прерывание 5 запрограммировано таким образом, чтобы вы­ дать содержимое экрана на принтер. Но вектор прерывания может указывать на процедуру, предназначенную для совершенно другой цели. Например, изощренная программа имитации, которой требу­ ются часы для завершения своей работы, может быть прервана в любое время комбинацией «Shift>> + <<PrtSc,> для выдачи рапорта о текущем состоянии расчетов. Вы можете также захотеть, чтобы на принтер была послана копия графического экрана. «PrtSc» обеспе­ чивает доступ к программе, которая находится резидентно в памяти во время загрузки MS-DOS [1.3 .4 ]. Такая стратегия позво­ лит вам написать утилиту, которая будет в состоянии работать из другого программного обеспечения. Низкий уровень Здесь приведена основная форма пе·репрограммирования проце­ дуры. Не забудьте восстановить оригинальный вектор прерывания (F000:FF54) при завершении программы. Если вы забудете сделать это, то все пойдет нормально до тех пор, пока не будет нажа1:а комбинация <<Shift,> + <<PrtSc,>, а тогда произойдет сбой системы
клавиатура 151 (более полный пример программирования прерывания см. в [l.2.3 ]). ;---изменить вектор прсрываш1я для «PrtSc» CLI ;запрет прерываний MOV AX,SEG :- .:_ROUT ;1ю:1учасм а;1рсс 11р<щ~;1ур1,i MOV DS,AX MOV DX,OFFSET N_ROUT MOV AL,S . ;номер изменяемого вектора MOV АН,25Н ;номер функцш1 INT 21Н ;изменяем вектор STI ;разрешаем nрерыоаш1я ;---описание процедуры «PrtSc, . N_ROUT PROC FAR SТI PUSH АХ MOV СХ,100 РОР АХ IRET N_ROUT ENDP ;разрешаем nрерыоаш1я ;сохраняем регистры ;паша процедура ;воссташ11типаем регистры ;возврат ~•з прер1,111аш1я 3.3. Сводка кодов клавнш н нх назначен не Различные коды • клавиш и коды символов могут приводить к недоразумениям. В приведенных ниже таблицах все они перечислены. Обратите внимание на следующие аномалии; - клавиша «Ins» является единственной, которая при нажатии как выдает код символа в буфер клавиатуры, так 11 меняет статус регистра клавиш-переключателей; - - имеется 4 кода ASCII, которые могут быть по,1учены двумя способами. ASCII 8 - нажатием клавиш <,Backspacc,> и Ctrl-H, ASCII 9 - -клавиш «ТаЬ» и Ctгl-1, ASCII 13 - клавиш «Entcr» 11 Ctrl-M, а ASCII 27 - клавиш «Esc>> и Ctrl-[; - символы, соответствующие 32 управляющим кодам ASCII , he выводятся на экран при использовании функций ввода с кла­ виатуры, обеспечивающих автоматическое •эхо. Они могут быть
IS.2 Глава 3 выведены либо с помощью функции l0H прерывания \ОН, либо прямым выводом в память дисплея (оба способа· обсуждаются в [4.3 .l ]); - комбинации клавиши <<Ctrl,> с буквами алфавита генерируют однобайтовые коды ASCII. Все остальные комбинации <<Ctrl» гене­ рируют двухбайтовые (расширенные) кЬды; - клавиша «5» дополнительной клавиатуры не действует, если установлен режим управления курсором клавишей «NumLock»; - комбинации <<Shift-PrtSC>> и <<Ctrl-Alt» (а также «SysReq» для АТ) это те редкие случаи, когда комбинация клавиш приводит к немедленному вызову некоторой процедуры. Из них только первая перепрограммируема. Прерывание обработки «Ctrl-Break» (также перепрограммируемое) вызывается только тогда, когда статус <<Ctrl-Break,> обнаружен процедурой MS-DOS; - любой код ASCII, кроме О, может быть введен путем нажатия клавиши «Alt», набора кода -ASCII на дополнительной клавиатуре и затем отпускания клавиши «Alt». Поскольку код О исключен, расширенные коды не могут быть введены таким споtобом. • Отметим, что вы практически ничего не можете сделать, чтобы преодолеть ограничения, накладываемые на недопустимые комбинации клавиш. Например, нельзя определить комбинацию Ctrl + CursorUp, принимая код <<CursorUp,>, а затем проверяя регистр статуса переключателей для определения того, была ли нажата клавиша <<Ctrl». Если «Ctrl>> была нажата, то клавиша «CursorUp» вообще не выдает никакого кода. 3.3 .1 . Предопределенное н~пользованне клавиш , Относительно клавиш имеется ряд соглашений, которые должны выполняться всеми программами. Они приведены в техни­ ческом руководстве, и если программисты будут придерживаться их, то пользователю будет легко переходить от одной программы к другой. Заметим, однако, что программное обеспечение самой фирмы IВМ не всегда следует этим соглашениям. Соглашения таковы: ScrollLock CТRL 4/6 Pg Up Pg Dn CТRL END Переключает режим вывода на терминал, при котором. перемещение курсора сдвигает экран, а не сам курсор. Сдвигает курсор на слово влево/вправо. Дру1-ая воз- можность; горизонталы,ыи С,'\n~н· экрана на поз~щ~•ю табуляции nлеnо/вправо. Возврат на 25 строк назад. Сдвиг на 25 строк вперед. Удаление текста от позиции курсора до конца строки.
клавиатура CТRL PgDn НОМЕ CТRL НОМЕ END 153 Удаление текста от позиции курсора до конца экрана. В тексте перемещает курсор к началу строки ИJIИ документа. В меню возвращает в главное меню. Чистит экран и помещает курсор в левый верхний угол. Перемещает курсор к концу строки иш1 к концу документа. BACKSPACE/DELETE DEl,ETE уни•пожает символ, на который указывает курсор, и сдвигает остаток строк~~ на од11у позицию uлево, BACKSPACE удаляет символ слева от курсора и делает :ro же самое.' INS ТАВ/ВАСКТАВ ESC Переключает реж~iм вставки/замены. Перемещает курсор в следующую позицию табуляци•1: впраuо - если была нажата одна «ТаЬ,., tt влево - если вместе с клавишей Shift. Выход из программы или процедуры. 3.3~2. Сводная табnнца скан-кодов Каждая клавиша генерирует два типа скан-кодов: "код нажатия", когда клавиша нажимается, и "код освобождения", когда клавиша отпускается. Для всех машин, кроме АТ, код осво­ бождения на 128 больше кода нажатия (бит 7 = 1). Таким обра­ зом, клавиша Т создает код 20 при нажатии и код 148 при отпус­ кании. АТ использует одну и ту же цепочку битов для кодов нажатия и освобождения, но коды освобождения состоят из двух байтов, первый из которых всегда равен 0F0H. PCjr имеет специ­ альный скан-код мнимой клавиши, номер 55. Этот код порож­ дается, когда одновременно нажаты три или более клавиш, что помогает избежать ошибок при вводе. Прерывание клавиатуры отбрасывает этот код, и он не связывается ни с каким кодом ASCII или расширенным кодом. Клавиши пиш~ацей машинки Клавиша Ко8 нажатия Кла,тша Ко8 нажатия Клав11111а Ко8 нажатия "1" 2 "Т" 20 "L" 38 "2" 3 "У" 21 ···" 39 "3" 4 "U" 22 40 "4" 5 "1" 23 41 "5" 6 "О•• 24 "\" 43 "6" 7 "Р" 25 "Z" 44
154 Глава 3 Клавиша Код нажатия Клавиша Ко/! нажатия Клавиша Ко/! нажатия ,.. ,.,, 8 .. [" 26 "Х" 45 ''8" 9 '" ]" 27 "С" 46 "9" 10 "А" 30 "V" 47 "О" 11 "S" 31 "В" 48 ",. 12 "D" - 32 "N" 49 .. ;" 13 "F" 33 "М" 50 "Q" 16 ''G" 34 .... 51 . "W" 17 "Н" 35 "" 52 ''Е" 18 "J" 36 "/" 53 "R" 19 "К" 3,7 пробел 51 Ущ;~авляюш;ие клавиши Esc Ctrl 29 Alt 56 Backspace 14 lert shiFt 42 CapsLock 58 ТаЬ 15 rlght shirt 54 NumLock 58 Enter 28 PrtSc 55 ScrollLock 70 Функциональные клавиши Fl 59 F5 63 F9 67 F2 60 F6 64 Fl0 68 FЗ 61 F7 65 F4 62 F8 66 Клавиши дополнительной клавиатуры ' "7" 71 "S" 76 ''3'' 81 "8" 72 "6" 77 "О" 82 "9" 73 "+ ,, 78 "" 83 "" 74 "1" 79 Sys Req 132 (только АТ) ''4" 15 "2" 80 мнимая55 (только PCjr) 3.3.3. Сводная таблица кодов ASCII Номера кодов от О до 31, управляющих кодов, обсуждаются более детально в (7.1 .9]. Напоминаем, что любой код ASCII от 1 до 255 может быть введен с клавиатуры, если держать нажатой клавишу «Alt» при наборе номера кода на дополнительной клавиатуре (с соответственно установленным режимом «NumLock»). Когда клавиша «Alt» освобождается, код вводится.
Клавиатура 155 Символ Десятичное Шестнадцатиричное Двоичное. , {null) о 00 00000000 © 1 01 0000000·1 • 2 02 00000010 ., 3 03 00000011 • 4 04 00000100 · + 5 05 00000101 · • б Об 00000110 • 7 07 00000111 а 8 08 00001000 о 9 09 00001001 ■. 10 ОА 00001010 d · 11 ов 00001011 ~ 12 ос 00001100 r 13 0D . 00 001101 ·л 14 ОЕ 00001110 q 15· OF 00001111 .. 16 ,10 00010000 ◄ 17 11 00010001 i 18 12 00010010 п 19 13 00010011 .., л 20 ·14 00010100 § •21 15 00010101 - 22 16 00010110 l. 23 17, 00010111 t 24 ·18 00011000 i 25 19 00011001 - 26 1А 00011010 - 27 1В 00011011 L. 28 1С 00011100 - 29 1D 00011101 .... 3D 1Е 00011110 у 31 1F 00011111 (space) 32 20 00100000 ! .33 21 00100001 ,, 34 22 00100010 # 35 23 00100011 $ 36 24 00100100 % 37 25 00100101 ~ 38 26 • 00100110
156 Глава '3 •Символ Десятичное Шестнадцатиричное Двоичное 39 27 00100111 ( 40 •28 00101000 ) 41 29 00101001 • 42 2А 00101010 + 43 2В 00101011 44 2С 00101100 45 20· 00101101 46 ?.Е • 00101110 / 47 2F 00101111 о 48 30 00110000 1 49 31 00110001 2 50, 32 00110010 3 51 33 00110011 4 52 34 00110100 5 53 35 00110101 6 54 36 00110110 .7 55 37 00110111 8 56 38 00111000 9 57 39' 00111001 58 ЗА 00111010 59 зв 00111011 < 60 зс 00111100 = 61 3D- 00111101 > 62 ·зЕ , 00111110 ? 63 ЗF 00111111 @ 64 40 01000000 Д. 65 41 01000001 в 66 42• 01000010 с 67 43 01000011 / D 68 44 01000100 •\ .Е 69 45 01000101 F 70 46 01000110 ·G 71• 47 01000111 н 72 48 01001000 1 73 49 01001001 \J 74 4А 01001010 к 75 4В 01001011 L 76 4С 01001100 М· 77 4D 01.001101
~> > 157 ", кл,авиа,μура ~ Символ Десятичное Шестнадцаrиричное Двоичное N 78 4Е 01001110 о 79 4F 01001111 р 80 50 01010000 Q 81 51 01010001 R 82 52 01010010 s 83 53 01010011 т ·84 54 01010100 u· 85 55 01010101 V 86 56 01010110 w 87 57 01010111 х 88 58 01011000 у 89 59 01011001 z 90 5А 01011010 [ 91 5В 010110Н ' 92 5С 01011100 J 93 5D 01011101 л 94 5Е 01011110 95 5F 01011111 96 60 01100000 а 97 61 01100001 ь 98 62 01100010 с 99 63 01100011 d 100 64 01100100 е 101 65 01100101 .f' 102 66 01100110 ~ 103 67 01100111 104 68 01101000 i 105 69 0,1101001 ~ 106 6А 01101010 107 6В 01101011 1 1.08 6С 01101100 m 109 6D 01101101 n 110 6Е . 01101110 о 111 6F 01101111 р 112 70 01110000 · q 113 71 01110001 r 114 72 01110010 s 115 73 01110011 t ·116 74 01110100 '. ,.,,, ,;-, ~; ,1;,'".'
158 Глава 3 Символ Десятичное Шестн&дцатирич~ое Д1:.1оичиое u 117 •75 .. 01110101 V 118 76 01110110 w 119 77 01110111 х 120 78. 01111000 у 121 79 01111001 z . 122 7А 01111010 { 123 7В 01111011 ' · 124 7С 01111100 1 } 125 7D 011,11101 ~ 126 7Е 01111110 о • 127 7F 01111111 с 128 80 10000000 'u 129 81 10000001 е 130 82 10000010 ' а 131 83 10000011 а 132 84 10000100 а 133 85 10000101 о а 134 86 ! 10000110 /. с 135 87 10000111 " 136 88 10001000 е .е 137 89 •10001001 е 138 8А 10001010 j' 139 8В 10001011 .. 140 ас 10001100 i ' 141 80 10001101 1 ~ 142 8Е 10001110 ~ 143 8F 10001111 Е 144 90 10010000 ~ 145 91 10010001 k 146 92 10010010 А о 147 93 10010011 о 148 94 10010100 ' 149 95 10010101 о " 150 96 10010110 u u 151 97 10010111 у 152 98 10011000
клавиатура 159 Символ Десятичное Шестнадцати~ичное Двоичное Q 153 99 10011001 u 154 9А 10011010 ф 155 9В 10011011 • ( 156 9С 10011100 • 157 9D 10011101 Pt 158 9Е 10011110 ! 159 9F 10011111 а 160 АО 10100000 , 161 А1 10100001. 1 б 162 А2 10100010 u 163 АЗ 10100011 n 164 А4 10100100 N 165 А5 10100101 а 166 А6 10100110 - о 167 А7 10100111 .i 168 АВ 10101000 r-- 169 А9 10101001 --, 170 АА 10101010 1/2 171 АВ 10101011 1/4 172 АС 10101100 1 173 AD 10101101 « 174 АЕ 10101110 » 175 AF 10101111 176 во 10110000 ... 177 В1 10110001 , ..... ::::=:~:: ~ 178 В2 10110010 1 179 вз 10110011 -1 180 В4· 10110100 =1 181 В5 10110101 -11 182 В6 10110110 .,. 183 В7 10110111 =i 184 В8 10111000 "11 185 В9 10111001 11 186 ВА 10111010 =n 187 вв 10111011 ::!.1 188 вс 10111100 ..u 189 BD 10111101 ... 190 ВЕ 10111110 .., 191 BF 10111111 {·,
' 160 Глава 3 Символ Десятичнье ШестнадцаТИQИЧНОе Двоичное . L 192 со 11000000 ..J... 193 С1 11000001 .,... 194 С2 11000010 ~ 195 С3 11000011 196 С4 11000100 + 197 С5 11000101 . 1= 198 С6 11000110 ,~ 199 С7. 11000111 1k 200 св 11001000 lf 201 С9 11001001 ::!.!:: 202 СА 11001010 :;;:: 203 св 11001011 \~ 204 се 11001100 - 205 CD 11001101 ~~ 206 СЕ 11001110 ~ ~ 207 CF 11001111 j ...u.. ·. 208 DO 11010000 j ::;:: 209 D1 11010001 .! ,r 210 D2 11010010 ,i, IJ... 211 D3 •1f010011 1 1::: 212 D4 11010100 i F 213 D5 11010101 (j rг 214 D6 11010110 - ft- 215 D7 11010111 * 216 D8 11011000 _J 217 D9 11011001 г 218 DA 11011010 • 219 DB 11011011 - 220 DC 11011100 1 221 DD 11011101 1 . 222 DE 11011110 - 223 DF 11011111 а 224 ЕО 11100000 ~- 225 Е1 11100001 r 226 Е2 11100010 1 n 227 ЕЗ· 11100011 r 228 Е4 11100100 о 229 Е5 11100101 . μ 230 Е6 11100110
Кл.авиа тура Символ Десятичное т 231 . Q 232 (, -е- 233 n 234 6 235 00 236 fll 237 ~ 238 n 239 - 240 ± 241 ;;,: 242 ~ 243 · r 244 J 245 . 246 '. ,_, . 247 о 248 • 249 ..; 250 •. 251 n 252 2 253 • 254 (Ыank 'FF') 255 б Р. джордейн "t 161 Шестнадцатиричное Двоичное Е7 11100111 Е8 11101000 • Е9 11101001 ЕА 11101010 -ЕВ 11101011 ЕС 11101100 ED 11101101 ЕЕ 11101110 EF 11101111 FO 11110000 F1 11110001 F2 11110010 F3 11110011 F4 11110100 F5 11110101 F6 11110110 F7 11110111 -F8 11111000 F9 11111001 FA 11111010 FB 11111011, FC 11111100 FD 11111101 ,FE 11111110 FF 11111111
162 Глава З 3.Э.4. Сводка кодов nсевд~~рафикн дnя построения рамок Ниже приведены номера кодов ASCII для символов псевдогра­ фики, используемых при построении линий и рамок. 218 194 191 213 209 184 195 197 180 198 216 181 179 192 193 217 212 207 190 _ .196 · 205= 214 210 183 201 203 187 199 215 182 211 208 189 200 202 188
Клавиатура 3.3 .S . Сводная таблица расширеннь1х кодов Значение 2-го байта 6* 15 16-25 30-38 44-50 59-68 71 1;, 73 75 77 79 80 81 82 83 84-93 94-103 104-113 114 115 116 117 118 119 120-131 132 Соответст11vющне к;щnи1ш1 Shift + ТаЬ ("back-tab") Alt+Q-Alt+Р(nерхннйрядбукn) Alt+А - .Alt +L (средний ряд букn) Alt+Z-Alt+М(нижнийрядбукв) Функцноналы1ые клавиши Fl - FI0 Home C11Гsor-11p (стрелка вверх) PgUp CLtrsor-leГt (стрел111а мс,ю) CLtrsor-right (стрелка nnpa110) End Cursor-down (стрелка вниз) PgDn Ins Del Fl-Fl0 + Shift Fl-Fl0 + Ct1·l Fl-Fl0 + Alt Ctrl + PrtSc Ctrl + Cursor-left Ctrl + Cursor-right c·trl + End Ctrl + PgDn Ctrl + Ноте Alt+1-Alt+ Ctrl + PgUp (верхний ряд) 163
Глава 4. Вывод на терминал 4.~. Управяенне выводом на термнная В этой главе рассмотрены монохромный адаптер, цветной графический адаптер, видеосистема PCjr и улуч­ шенный графический адартер (EGA). Все 4 системы базируются на микросхеме Motorola 6845 CRTC (cathode rc,ly tube controller); хотя EGA на самом деле использует заказную микросхему, основанную на принципах микросхемы 6845. Эта микросхема выполняет массу технических задач, которые обычно не интересуют программиста. Однако она также устанавливает режим ;экрана, управляет кур­ сором и (для цветного графического адаптера) цветом. Микросхема легко программируется напрямую, хотя процедуры операционной системы позволяют управлять большинством ее действий. PCjr имеет вспомогательную микросхему для дисплея "video gate array" (массив ворот дисплея), которая рассматривается в этом разделе вместе с микросхемой 6845. Архитектура EGA отличается от всех остальных адаптеров, поэтому EGA обсуждается отдельно. Среди не-ЕGА систем существует совместимость по использованию адре­ сов портов, но есть и существенные различия. Некоторые адреса портов EGA такие же, как и у других систем. Все в~iдеосистемы используют буфера, в которые отображаются данные для изобра­ жения на экране. Экран периодически обновляется· сканированием этих данных. Размер и расположение буферов меняется с сис­ темой, а также режимом экрана, количеством заранее отведенной памяти. Когда в буфере хранится несколько образов экрана, каж­ дый отдельный образ называют дисплейной страницей.
. Вывод на терминал 165 Монохромный адаптер. Монохромный адаптер имеет 4К байт памяти на плате, н,ачиная с адреса ВООООН (т.е. ВООО:0000). Этой •памяти хватает только для хранения одной 80-символьной · стра­ ницы текста. Цветной графический адаптер. Цветной графический адаптер имеет 1бК . байт памяти на плате, начиная с адреса памяти В8000Н. Этоrо достаточно для отображения одного графического экрана, без страниц, или для отображения от четырех до восьми экранов текста в зависимости от числа символов в строке - 40 или 80. • PCjr. PCjr имеет видеосистему, которая на самом деле является улучшенной версией цветного графического адаптера. Она уни­ кальна тем, что использует для видеобуфера обычную опера­ тивную память системы. Когда BIOS инициализирует систему, верхние 16К установленной памяти отводятся под буфер терми­ нала. Таким образом, адрес буфера зависит от того, сколько памяти в системе. Для добавочных дисплейных страниц могут быть отведены блоки памяти в других местах, а также начальный объем может быть уменьшен до 4К и поддерживать только один экран текста. • EGA. EGA может быть снабжен 64К, 128К или 256К памяти. Кроме использования в качестве· видеобуфера; эта память может также хранить битовые описания вплоть до 1024 символов (как объяснено в (4.3.4 ]). Стартовый адрес буфера дисплея программи­ руемый, поэтому буфер начинается с адреса АОООН для -улуч­ шенных графических режимов и с ВОООН и 88001-t для совмести­ мости со стандартнымц монохромным и цветным графическим режимами. В большинстве случаев EGA занимает два сегмента с адресами от АОООН до BFFFH, даже когда имеется 256К памяти. Это возможно, поскольку в некоторых режимах два или более байтов памяти дисплея считываются из одних и тех же адресов. Доступнре число страниц зависит как от режима экрана, так и от количества имеющейся памяти. Вследствие своей сложности EGA имеет ПЗУ на 16К.байт, которое заменяет и расширяет процедvры работы с терминалом ВIOS. Начало • области ПЗУ - адрес СООО:0000. В текстовых режимах буфера начинаются с данных для верх­ ней строки экрана от левого угла. Дал1,нейшие данные переносятся с правого конца одной строки на левый конец следующей, как будто экран представляется одной большой строкой - с точки зрения видеобуфера так оно и есть. Однако в графических режи­ мах буфер может быть разделен на 2 или 4 части .. У цветного графического адаптера и PCjr различные части буфера содержат информацию, относящуюся к каждой второй или каждой четвертой линии точек на экране. У EGA каждая часть буфера
166 Гл:.~ва 4 содержит один бит из двух или четырех, которые определяют цвет данной точки ::>крана. . При выводе текста различные видеосистемы работают одина­ :-шво. Для ::>крана отводится 40nn байт, так что на каждую из 2000 rюзиций экрана nрихо-дится 7 r,айт:.1 (25 строк х 80 символов). Псрвыii байт содержит ;сед ASCII. Аппаратура дисплея преобразует номер кода ASCH в связанный с ним символ и посылает его на ::>кран. Второй байт (байт атрибутов) содержит информацию о том, как должен быть выведен данный символ. Для монохромного дисплея он устанавливает, будет ли данный символ подчеркнут, выделен яркостью или негативом, либо применяется' комбинация этих атрибутов. В цветовых системах байт атрибутов устанавливает основной'и фоновый цвета символа. В любом случае ваша программа может писать данные прямо в буфер терминала, что значительно повышает скорость вывода на ::,кран. Все системы, кроме монохромной, предоставляют набор цвет­ ных графических режимов, которые отличаются как разрешением, так и числом одновременно выводимых цветов. И PCjr, и EGA • могут одновременно выводить 16 цветов, причем EGA может выбирап, эти· 16 из набора 64 цветов. При испол1,зовани11 16 цветов каждая точка ::>крана требует четырех бит памяти, посколъку 4 бита могут хранить числа от О до 15. По аналогии четырехцветная графика требует только 2 бита на то,,ку. При. двvхцветной графике представление восьми точек может быт1, уп:1ковано в один байт видсобуфера. Коли,1ество памяти, требу­ емое для данного режима ::>крана, легко вы,111сл~1т1,, если известно количество выводимых в ::>том режиме точек и количество битов, необходимое для описания одной точки. Текст легко комбини­ руется с графикой (BIOS рисует симво,1ы на графическом экране), и вы можете создавать свои спсциат,ные символы. 4.1 .1. Программнрованне контроллера дисплея 6845 Все видеосистемы строятся вокруг микросхемы контроллера видеотерминала Motorola 6845 (EGA использует заказную микро­ схему, основанную на микросхеме 6845). Применение микросхемы, во многом идентично в адаптерах: монохромном, цветном и PCjr; но EGA сил1,но отличается от других адаптеров и по ::>той причине мы рекомендуем избегать прямого программирования микросхемы, когда BIOS может выполн11ть работу за вас. Говоря общими сло­ вами, микросхема 6845 устанавливает видеодисплсй в один из алфавитно-цифровых или графнчсских режимов. Она выполняет основную работу по интерпретации номеров кодов ASC!I и поиску данных для вывода соответствующих символов в J\iикросхсмс ПЗУ
Вывод на термиrшл 167 (а иногда в оперативной памяти), декодирует значения атрибутов цвета и соответственно устанавливает экран, а также создает курсор и управляет им. В архитектуре EGA часть этих функций распределена между другими-микросхемами. Микросхема 6845 имеет 18 управляющих регистров, пронуме­ рованных от О до 17. Первые 1О регистров фи~сируют горизон­ тальные и вертикальные параметры дисплея. Эти регистры, как правило, неинтересны для программистов, поскольку они автома­ тически устанавливаются BIOS при изменении режима экрана. Нс советуем экспериментировать с этими регистрами, поскольку можно испортить терминал. Регистры имеют размер 8 бит, но некоторые связаны в пары, чтобы хранить 16-битовые величины. Пары #10-11 и #14-15 устанавливают форму [4.2 .4] и местопо­ ложение [4.2.1] курсора. Пара #12-13 управляет страницами дисплея [4.5 .3 ]. Пара # 16-17 сообщает позицию светового пера [7.3.2 ]. Большинство регистров доступно. только . для записи; регистр адреса курсора можно и читать, и писать, а регистр свето­ вого пера предназначен только для чтения. EGA имеет 6 доба­ вочных регистров, которые связаны с техническими деталями. Р.сrистр 20 наиболее интересен; он определяет, какая линия скани­ рования в строке символа используется для подчеркивания. Доступ ко всем 18 реrистрам осуществляется через один и тот же порт, адрес которого 'для монохромного адаптера - 3В5Н, для цветного адаптера и PCjr - 3D5H (заметим, что вес адреса портов для монохромного адаптера такие же, как и дЛS\ цветного, за исключением того, что средней цифрой является В, а нс D). EGA использует один из этих двух адресов в зависимости от того, присоединен ли к нем,у цветной или монохромный монитор. Для записи в регистр монохромного адаптера надо сначала в регистр адреса, •расположенный в rюрте 3В4Н (3D4H для цветного), послать номер требуемого рспктра. Тогда следующий байт, пос­ ланный в порт с адресом 3В5Н, будет записан в этот регистр. Поскольку регистры, интересные для программиста, испол1,зуются попарно, надо сначала заn11са'Т1, в адресный регистр, потом в первый регистр пары, потом снова в адресный регистр и, наконец, во второй регистр пары. Адреса портов смежные, nо;)Тому легче всего_ адресовать их, используя инструкции INC и DEC, как в следующем примере: ;---запись в регисфы 11 и 12 микросхемы 6845 (данные в ВХ) ;---выбираем регистр младшего байта MOV DX,3B4H ;лорт aдpect4oro регистра MOV AL,11 OUT DX,AL ;номер регистра для младшего байта ;nось,лаем номер регистра
168 ;-:,-пось1паем байт INC DX MOV AL,BL . ;увеличиваем адрес порта ;бе?ем ~падший байт OUT DX,AL • ;посыпаем его в регистр 11 ;'---выбираем регистр старwщ-о байта DEC DX MOV AL,12 OUT DX,AL ;---посыпаем байт INC DX MOV AL,BH OUT DX,AL 1 ;восстанавпиваем адрес порта ;номер регистра дп11 старшего байта ;посы11аем номер регистра ;увепичиваем адрес порта ;берем старший бс1йт ;пось1паем его в регистр 12 Глава 4 У монохромною и цветною адаптеров имеются еще три порта, которые важны для программистов. Они имеют адреса ЗВSН, 389Н и ЗВАН для монохромною и ЗDSH, 3D9H и ЗDАН - для цветною адаптера. Первый устанавливает режим экрана, второй связан в основном с установкой цветов экрана, а третий сообщает полезную информацию о статусе дисплея. . PCjr использует не все эти адреса аналогичным образом. Вместо этоrо он держит часть информации, относящейся к портам, в микросхеме массива ворот дисплея, основное назначение которой обеспечить дQполнительное управление цветами экрана. Доступ к массиву воррт дисплея, осуществляется через порт с адресом ЗDАН. У цветною адаптера этот порт возвращает байт статуса; у PCjr этот порт также возвращает байт статуса при использовании инст­ рукции IN, но он предоставляет доступ к массиву ворот, когда используется инструкция OUT. Массив ворот дисп}\ея имеет следующие регистры:: Нпмер о 1 2 3 4 IOH-IFH Наз1ш 11е1111е режим упраw1снш1 1 маска ,на~р:~ 11встоп (па;1итр1,1) цвет rрашщы реж~,м упрШIЛСIIИЯ 2 сброс 1шзначеш1с llRl~l 'OII !ШЛИТРl>I Доступ ко всем реmстра~ осуществляется через порт ЗDАН. Сначала надо послать в этот порт номер требуемого регистра, а затем - значение регистра. Порт автоматически переключается '-tежду функциями работы с адр~ами и с данными. Чтобы он ожи-
Вывод на терминал 169 дал ввод адреса, надо прочитать из него. Отдельные регистры обсуждаются по мере изложения-материала этой главы. Особый интерес представляют 16 .регистров палитры с номерами (УГ l0H до 1FH. Каждый регистр имеет размер всего 4 бита, этого достаточно, чтобы хранить 16 кодовых номеров для 16 возможных цветов. Для каждой позиции символа или точки на экране видеобуфер содержит данные, указывающие, каким цветом должен выводиться объект. Такую информацию называют дан- . ными атрибутов. В отличие от цветного графического адаптера РСjг не использует данные атрибутов для непосредственного опре­ деления цвета, который будет выводиться. Вместо этого данные атри.бутов являются указателями на один из 16 регистров палитры, а число, содержащееся в регистре, определяет, каким цветом будет выводиться данный символ. При таком методе программе нужно изменить только установку регистра палитры, и все символы и,,и точки с соответствующим атрибутом изменят свой цвет. Регистры палитры работают во всех· режимах как текстовых, так и графических. EGA распределяет эти функции между микросхемой контрол­ лера атрибутов (адрес порта ЗСОН) и двумя микросхемами конт­ роллера графики (адреса портов ЗССН-ЗСFН>. Контроллер атри­ бутов содержит 16 регистров палитры EGA, пронумерованных от 00 до OFH. Эти регистры могут содержать 6-битовые коды цветов, :когда EGA связан с улучшенным цветным дисплеем, по:)Тому могут быть использованы любы'с 16 цветов из набора 64. В [4.4.1] показано, как программировать регистры палитры д,,я PCjr и EGA. 4.1 .1. Установка/проверка режима ,gис:плея Монохромный адаптер поддерживает один режим терминала, цветной графический - семь, PCjr - десять, а EGA - двенадцать. Система РСjг более гибкая, чем монохромный или цветной адап- • теры, поскольку она предоставляет широкцй выбор цветов в режи­ мах с двумя и четырьмя цветами, а также серые тени в черно­ белом режиме. EGA еще более сложен, он поддерживает палитру из 64 цветов, графику на монохромном дисплее• и вывод в 43 строки. Ниже приведен перечень различных режимов: Номер о 1 2 3 Режим Драптrры 40*25(320*200) B&W алфашп110-ц~1фрово11 цветной, РСjг, EGA 40*25 (320*200) цве-гной алфавишо-цифровой цвепюй, PCjr, EGA 80*25 (640•200) B&W алфавитно-щ1фровой цвешой, PCjr, EGA 80*25 (640•200> цветной алфавиТf!о-цифровой цветной, PCjr, EGA
170 Глава 4 4 320*200 4-цnстная 1·рафf1ка цветной, l'Cjr, EGA 5 320*200 B&W графf1ка (4 тени на l'Cj1·) ц11етной, l'Cjr, EGA 6 640*200 B&W 1-рафика ц11етной, l'Cjr, EGA 7 80*25 (720*350) B&W алфа11нпю-1н1фро11ой монохромный, F:Gд· 8 160*200 16-цвепшя ~·рафика PC_ir 9 320*200 16-цве"Fная ~·рафика l'Cjr л 640*2004-цнсшая 1·рафf1ка l'Cjr n зарезсрIтроI~а11 для ЕGЛ с зарезср11нрощ111 для ЕGЛ D 320*200 16-цвстная 1·рафf1ка ЕGЛ Е 640*200 16-цветная графика ЕGЛ F 640*350 4-цпспшя 1·рафf1к51 Iш мо11охромIюм ЕGЛ 10 640*350 4- • шш l 6-ц11ет11ая графика ЕGЛ EGA разрешает иметь 8 страниц в режиме 7 - стандартном монохромном текстовом режиме. Режимы 0-6 полностью совмес­ тимы и используют память одинаковым образом. При условии, что переключатели на EGA установлены для р,1боты с улучшенным цветным дисплеем фирмы IВМ, традиционные текстовые режимы выводятся с высоким разрешением, а рисунок символов, состоит из 8*14 точек, а не из 8*8, как обычно. BIOS хранит однобайтовую переменную по адресу 0040:0049, в которой содержится номер текущего режима. Байт по адресу 0040:004А дает число символов в строке в текстовом режиме. Высокий уровень Бейсик использует операторы SCREEN и WIDTH для управ­ ления режимом экрана. PCjr применяет эти операторы несколько другим способом, чем монохромный и цветной адаптеры, и это будет обсуждаться ниже. Один оператор SCREEN устанавливает режим для цветного адаптера. За оператором стоит номер кода, устанавливающий разрешение, где: о 1 2 тексто11ый режим графf1ческнй режfIм средIIеI·0 разрешс11ю1 графическf1Й режим 11ысокоГ(J разрешешIя SCREEN l устанавливает графический режим среднего разре­ шения. Второй параметр включает и выключает цвет. Этот пара­ метр не имеет смысла для режима высокого разрешения на цвет-
Вывод 11а тер.мшшл 171 ном адаптере, поскою,ку ра;,;рсшен, тол~,ко черно-белый рсж11м. Для текстов1,1х режимСF! О в качестве второго пара~1етра вык.1ю­ част цвет, а I ВКJJючаст. Оператор SCREEN 0,0 устанав.,иваст текстовый черно-белый режим. Для графического режимй ситу­ ация обратная: О включает цвет, а I выключает. Поэтому опера­ тор SCREEN 1, 1 устанавливает черно-белый графический режим среднего разрешения. . Вес, реж_имы первоначально выводятся черно-белыми. Чтобы закрасить экран фоновым цветом, должен быт1, 1кпол1,зо11-.1н опе­ ратор COLOR (см. [ 4.1 .J 1>. В графическом режиме одного опера­ тора COLOR достаточно, чтобы изме1шт1, вес,, фон на указанный цвет. Но для текстового режима вы должны после оператора COLOR перейти к оператору CLS. • В текстовых режимах в строке может быт~, 40 или 80 символов. Для установки требуемого числа символов в строке нужен опера­ тор WIDTH. WIDTH 40 даст 40 символов в строке, а WIDTH 80 - 80. Другие зщ1чсния недопустимы. Если оператор WIDTH испо~ь­ зуется в графическом режиме (SCREEN I или SCREEN 2), то WIDTH 40 переводит. ::>кран в режим среднего разрешения, а WIDTH 80 - в режим высокого разрешения. Вот нсскот,ко примеров: 100 SCREEN О, 1: WIDТl·I 40 ·11вспюй тскспJ111,1й реж нм с 40 снмво:1амн l(JO SCREEN 0,0: WIDTH 80 ·цветной р_нсп,,ей, в монохромном режиме 100 SCRIШN 1,0: I\IIC'IIШИ 1·рафнка C[)C,'\IICI '() разрс111с11н11 500 WIDТI 1 80 '11срсво,о1нм II рсж~1м 111,1c<жnixJ разрешении Монохромный монитор можс1· бып, переведен в режим 40 сим­ волов в строке операторами SCREEN О: WIDTH 40. Для восста­ новления режима с 80 символами введите WIDTH 80. В режиме с 40 символами они сохраняют свою обычную ширину, по::>тому будет использоваться только левая часть экр,Чfа. Строка перено­ сится после 40-ro столбца и невозможно поместит~, курсор в пра­ вую половину ::>крана с помощ1,ю оператора LOCATE. CLS ч11спп только левую часть ::>крана. Трудно представит~, программу, кото­ рая использовала бы это свойство, но оно действитет,но позво,1яст программе приниJ'~lать ввод (скажем, через оператор INPUT), в то время как пользователь продолжает пеЧ'атать в левой половине экрана, оставляя правую половину ::>крана для возможной коррек­ тировки вводимой информации. При этом любой вывод в правую половину экрана возможен только при прямом обращении к памяти дисплея, как объяснено в [4.3.1 k
172 В Бейсике PCjr применяется 7 номеров режимов: Номер о 1 2 3 4 5 6 Режим текстовый режим, ширина может быть 40 или 80 4-цветная графика среднего разрешения 2-цветная графика высокого разрешения 16-цветнаи графика низкого разрешения 4-цветный режим среднего разрешения 16-цветный режf1м среднего разрешенf1я 4-цветный режим высокого разрешения Глава 4 Последние четыре режима требуют дискеты с Бейсиком. Размер страницы определяет количество памяти для одного экрана (дисплейные страницы обсуждаются в [4.5.3 ]). Программа должна отвести соответствующее количество памяти 'перед установкой режима. Это делается оператором CLEAR. За оператором CLEAR долж5ы следовать три числа, определяющие· отводимую память, третье из этих чисел устанавливает размер видеобуфера (первые два параметра обсуждаются в [l.3 .1 )). Например, размер для видеобуфера 16К, устанавливаемый по умолчанию, выделяется комаlfДой CLEAR "16384. К сожалению, размер видеобуфера ука­ зывается в байтах, поэтому он не равен круглому числу типа 4000 или 32000, а равен 4096 или 32768. Помните, что 2К = 2·11, 4К = 2·12; 16К = 2·14, а 32К = 2·1s. Для выделения трех страниц по 16К введите CLEAR "3 х 2·14, Этот оператор должен поме­ щаться ,в самом начале программы, поскольку при использовании оператора CLEAR все переменные очищаются. Отметим также, что при создании нескольких страниц страница О начинается с млад­ ших адресов памяти. К моменту выхода этой книги Бейсик не поддерживает допол­ нительные режимы терминала EGA. В [4.3 .3] приведена подпрог­ рамма на машинном языке, которая позволит вам у,тановить эти режимы. Средний уровень Функция О прерывания l0H устанавливает режим дисплея. В AL должен находиться номер режима от О до А. Чтобы установить цветной графический режим среднего разрешения, надо: • моv дн,о· MOV AL,4 INT 10Н ;номер функц~.tм ;номер требуемого режима ;устанавл·мваем режим
вы.вод на терминал 173 Для определения текущего графического режима применяют функцию F прерывания 1ОН. Прерывание возвращ.1ет номер режима в AL. Оно также дает номер текущей страницы дисплея в ВН и число символов в строке в АН. MOV AH,0FH ;номер.функции· INT !ОН ;попучение информации о режиме дисплея MOV MODE_NUMBER,AL ;н~мер режима в AL MOV NUMBER_COLS,AH ;чиспо символов в строке в АН MOV CURRENT_PAGE,BH ;номер текущей страницы в ВН MS-DOS обеспечивает также Еsс-последовательности для уста-. новки и сброса режимов дисплея. Для ·этого необходимо предвари­ тельно загрузить драйвер ANSI.SYS, как объяснен6 в nриложени~ Д. Упрщшяющая строка имеет вид ESC [ = #h, где # - номер режима, указанный как код ASCII, а ESC обозначает один симвqл с кодом ASCII 27. Например: • • ;---в сегменте данных М RES COL DB MED_RES_B&W DB 27, '[ = 4h$' 27, '[ = 5h$' ;---устано~ка цветного графического режима среднего разрешения MOV АН,9 ;комер функцни вывода строки LEA DX,M_RES_COL ;QS:pX должны указывать на строку INT 21Н ;измененне режнма Низкий уровень В данном случае цветной .адаптер, монохромный адаптер и PCjr рассматриваются отдельно, поскольку они существенно разли­ чаются. Цветной графический адаптер име~т реmстр, который устанавливает режим дисплея. Он расположен в порте с адресом ЗD8Н. Биты О, 1, 2 и 4 хранят установку. Бит О устанавливает 40 символов в строке, когда он равен О, и 80, когда он равен 1. Бит 1 устанавливает дисплей в текстовый режим, когда он равен О, и в графический, когда он равен 1. Бит 2 устанавливает цветной . режим, когда· он равен О, и черно-белый, когда он равен 1. И наконец, бит 4 · устанавливает д.тiя графического режима среднее разрешение, когда он равен О, и' высокое разрешение, когда он равен 1 (бит 2 должен быть равен 1). Ниже приведены возможные комбинации: 1 1. 1
174 Режим Iiиты: О. 40*25, черно-белый, текст l. 40*25, цветной, текст . 2. 80*25, черно-белый, текст 3. 80*25, цветной, текст 4. 320*200, черно-белый, графика 5. 320*200, цветной, графика 6. 640*200, черно-белый; графика Глава 4 54321О О IОО о ооо 1О 1О оо оо о 1О1 ОО1 1 О о о о 1 текст 80*25 графика 320*200 •1ер1ю-бслый разрсше1111е вывода графика 640*200 миrюше Изменение этих битов нс приводит к изменению режима дисп­ лея. Нужно еще мноrо шагов, включающих изменение параметров первых 10 регистров по адресу порта 3D5H. BIOS заботится обо всем этом, и не имеет смысла занимап,ся подобно11 деятсл1,ностью. Однако иногда целесообразно ре11ющиализировап, регистр рсж11ма в ero текvщем режиме, изменяя биты З и 5, которые на самом деле не о~вечают за ·установку режима. Когда бит 5 сброшен в О, то он запрещает атрибут мигания символов; в этом случае, если старший бит байта атрибутов установлен, то это приводит к выводу фоновоrо цвета высокой интенсивност1,ю (см. пример в [4.1 .3 ]) . Бит 3 этого регистра управляет разрешением вывода. Когда он равен О, весь ::,кран закрашивается в цвет рамки, но видеобуфер не очищается. Вывод мгновенно возвра_Jдастся, когда значение этоrо бита меняется на 1. Это свойство позволяет избе­ жать интерференции экрана при сдвигах [4.5.\ ]. Некоторые ути­ литы используют cro для того, чтобы зря нс утомлs1т1, фосфорное покрытие трубки терминала, когда комп~,ютер включен, но нс используется. Отметим также, что два старших бита регистра нс принимаются во внимание. Соответствующий адрес порта монохромного адаптера - 3В8Н. Имеют значение только три бита. Бит О устанавливает высокое разрешение, которое является единственным допустимым режимом для монохромноrо дисплея. Если ::>тот бит равен О, то комт,ютср перестает работать. Два других значащих бита - :Jто биты 3 и 5,
Вывод на терминал 115 которые управляют разрешением вывода и миганием в точности так же, как и для цветного адаптера. PCjr распределяет информацию, содержащуюся в одном порте для монохромного и u.ветнQrо адаптеров. Массив ворот дисплея имеет два регистра режима, номер О и З. Для доступа к этим регистрам надо послать номер регистра в порт с адресом ЗDАН, а затем записать данные по тому же адресу (чтение этого порта обеспечивает восприятие первой записи в него как указание номера требуемого регистра). ,Вот значение битов этих регистров: Регистр О: бит О 1 2 з 4 Регистр 3: бит О 1 2 3 текст. 80*25 и режимы 5 и 6, юш•1с О rраф~1ческий режим" О = тскстоuый запрет цuетоu, О = разре111еш1е Ltueтoп "' разрешение μывода, О "' запрет uыuода 16-цuетный \)ежим, О = 11сс остальные реж~1м1,1 всегда О 1 = разрешение МИ~IIИЯ, о = 19 фо1ю11ых ЦIICTOII • всегда О \ 1 = 2-LЩСТ118Я, rраф~~а, 0 : DCC ОСТаJН,Ш,IС рСЖИМl>I ' Как и в двух предыду~'х случаях, не стоит· устанавливать регистры прямо из программ ,1 , т,1к как нужно еще много работы для программирования мик осхсмы 6845. Но каждый из двух регистров содержит бwт, кот рый иногда приходится прогрнммно модифицировать, а поскольку эти регистры только для записи, вам необходимо понимать значен~с всех их битов: бита разрешения вывода в регистре О и бита ра~решсния мигания в регистре 3. Их действие было описано ранее, \i возможное их использование еще не раз будет обсуждаться в это» главе (см. [4.5 .1 ) и [4.1.3 l>. • EGA имеет два регистра,\ управляющих режимом дисплея. Адрес порта первого - ЗDSH. Э\rот регистр нс содержит ни одного . бита, связанного с чем-либо др гим, поэтому нет никаких причин обращаться к нему. Второй ре истр имеет адрес порта 3СОН и содержит бит, который выбирае , будет ли ·бит 7 байта атрибутов соответствовать миганию или вы окой интенсивности. Этот вопрос обсуждается в [4.1.3 ]. •
176 Глава 4 4.1.3 . Установка атрибутов/цветов символов Когда дисплей установлен в текстовый режим в любой из видеосистем, каждой позиции символа на экране отводится два байта памяти. Первый байт содержит номер кода ASCII символа, а второй - атрибуты символа. Цветной адаптер и PCjr могут выво­ дить в цвете как сам символ, так и всю область, отведенную дан­ ному символу (фоновый цвет)'. Монохромный адаптер ограничен только черным и белым цветом, но он может генерировать под­ че13кнутые символы, чего не могут делать цветной адаптер и PCjr. Все три системы могут выдавать мигающие символы и негативное изображение. Они могут также создавать символы с выlокой иhтенсивностью, хотя для цветного адаптера и PCjr повышенная· интенсивность символа на самом деле приводит к другому цвету (восемь основных цветов имеют версии с повышенной интенсив­ ностью, что дает набор 16 цветов). EGA может делать все, что и остальные системы, а также многое другое. В частности, на улуч­ шенном дисплее он выводит подчеркнутые цветные символы, поскольку матрица изображения символов 8*14 предоставляет ТЗК) Ю ВОЗМОЖНОСТЬ. Атрибуты цвета Для указания цветов экрана одни и те же номера кодов используются в Бейсике и прерываниями операционной системы: О. - черный 1 - синий 2 - зеленый 3 - циан* 4 - красный 5 - магента* 6 - коричневый 7 - белый 8 - серый 9 - rо(]убой 1о - сrетло-зеле111,1й 11 - сретлый цшш 12 - fветло-красный 13 - i:ветлая магента 14 - желтый 15 - ярко-белый Младшие четыре бита байта атрибутов устанавливают цв~т самого символа (бит 3 включает высокую интенсивность). Следу­ ющие три бита устанавливают · фон ёимвола. И при обычных обстоятельствах старший бит вкiлючает н выключает мигание. Таким образом: * f' _ Цнсr маrе1па (magenta) приблизит ыю сuот11етсп1ует фиолетовому цвету, а циан (cvan) - голубому. - Примеч. llep.
Вы.вод на терминал 177 когда бит о 1, синий включае,-ся в основной ц~~ет 1 1, зелt,11ый nклю•шется II основ1ю~i ц1~ет 2 1, красный IIKJIIO'laeтcя n осношюй ЦIICT 3 1, СИМIIОЛ ш,шо;щтся с IIЫСОКОЙ HIП'CIICflllllOCТl,JO 4 1, сшшй 11к11ю•1астся n фо1ювый цвет 5 1, зелен1,1й включается о фоновый щ~ет 6 1, красный 11клю'lаетс11 11 фонош,,й ц11ст 71, СИМIIОЛЫ МШ'!Щ)Т Биты 0-2 и 4-6 содержат одни и те же компоненты цветов для· самих символов и фона .. Эти трехбитовые группы позволяют восемь возможных комбинаций. Когда включается бит высокой интенсивности, добавляются ~ще восемь цветов. Ulсстнадцать воз-, можных цветов получаются из этих установок битов tлсдующим образом: Инте11сишюст1, Кl!асный Зеленый С~ншй Низкая Высокая о о о •1срш,1й серый о о 1 CflllИЙ CIICTJIO-CИIШЙ о о зеленый CRCTJIO-ЗCJICIIЫЙ о 1 1 циан СIIСТЛЫЙ ЩШII о о крш;ш,,~i с1.1стло-крас11ый о 1 мш-с1па ,CIICTJIRЯ ма1-с1па о корн•111е111,1й желтый белый ярко-белый Можно иметь 16 цветов и для фоновоrо цвета. В этом случае бит 7 должен служить указателем высокоi1 интенсивности для фона, а не указателем мигания символов. Для цветного адаптера надо изменить бит 5 порта с адресом 3D8H в О, как показано ниже. Поскольку этот порт доступен только для записи, то все остальные биты должны быть переустановлены. Эта возможность доступна только в двух случаях: текстовых режимов с 40 и с 80 символами в строке. Для режима с 80 символами надо послать в порт число 9, а для режима с 40 символами - чисЛо 8. Чтобы вер­ нуть мигание, надо добавить к обоим значениям 32. Для PCjr надо сбросить в О бит 1 регистра 3 массива ворот дисплея. Все осталь­ ные биты должны быть равны нулю, кроме номера 3, который дол­ жен быть устанqвлен для режима двухцвет~ой графики. Кроме этоrо режима, для установки бита мигания надо сначала прочитать
178 Глава 4 порт с адресом ЗDАН, чтобы подготовить массив ворот диспле>1, • затем послать в него 3, чтобы указать регистр, и затем послать О, чтобы установить бит мигания. При завершении программы всегда надо восстанавливать мигание, так как следующая программа может полагаться на это. EGA также может разрешать/запрещать мигание, хотя в :,том случае адрес порта - ЗСОН. Сначала надо прочитать порт ЗDАН, чтобы получить доступ к адресному регистру в ЗСОН. Затем надо послать в ЗСОН l0H, чтобы указать соответствующий регистр. Наконец, надо послать данные по тому же адресу. Поскольку :,тот регистр только для записи, все биты должны быть прави.11,но уста­ новлены. Мигание включается установкой бита 3, а выкJ1ючается сбросом этоrо бита. Все остальные биты в цветном текстовом режиме должны быть равны О. Для цветного адаптера, коrда символы выводятся на дисплей в цветном графическом режиме, они изображаются в текущем фоновом цвете. Операторы, которые выводят на :,кран, как в Бейсике, так и в MS-DOS (прерывание 21 Н) ограничены выводом символов в третьем цвете используемой палитры (имеются две палитры из трех цветов, см. (4.4 .1 ]). В палитре О символ1>1 желтые/коричневые·, а в палитре I они белые. Процедуры вывода символов BIOS (прерыванJ1е !ОН), однако, моrут указат1, лю(5ой из трех цветов палитры. С другой стороны, для PCjr цвет, назна­ ченный определенной позиции палитры, может быт1, изменен, по:,­ тому для вывода символов моrут бып, использованы любые цвета. Для PCjr цвета, соответствующие данным кодоным номерам, моrут·быть изменены. Кажд1;1й кодовый номер связан с рсr11стром палитры в массиве ворот дисплея 14.1.11- Эти регистры прону­ мерованы от l0H до IFH, что соответствует кодам от О до 15. Каждый 4-битовый регистр содержит число в диапазоне 0-15, которое представляет реальный цвет, выводим1>1й, коrда оператор программы встречает один из кодовых номеров. Например, если в каком-то месте программы указано, что символ должен выводит1,сs1 с кодовым номером О, то цвет выводимого символа определяется кодом цвета, хранящимся в регистре палитры О. Начал~,ное значение этоrо регистра 0000, поэтому будет выводиться черный цвет. Но содержимое этого регистра может быть изменено, скажем на 0001, а в этом случае кодовый номер О приведен к выводу синим цветом. Кодовые номера, испот,зуемые в реrистр.~х палитры, такие же, как и в операторах проrрамм1,1. На рис. 4.1 показана начальная установка регистров палитры для всех регис­ тров, кроме регистра для зеленого цвета, который изменен так, чтобы выводился цвет маrента. Чтобы запрограммировать регистр 11алитры PCjr, нужно сначала послать его номер (от l0H до IFH) в массив ворот дис-
Вывод 11а тер.ми11ал Данн~е видеобуфера 0000 (черный) 0001 {синий) 001 О (зеленый) 0011 (циан) 0100 (красный) 0101 {магента) 0110 (коричневый) 0111 (белый) 1ООО (серый) 1001 (светло-синий) 101 О (светло-зеленый) 1011 (светлый циан) \ 1100 {светло-красный) 1101 (светлая магента) 111 О (желтый) 1111 {11рко-беJ\ый) Регистры палетты (регистр 02 был изменен) 0000 0001 0101 0011 - 0100 - 0101 0110 - 0111 - 1000 - 1001 1010 1011 1100 i,.,..-_..___ - 1101 1110 1111 ~.Р!!! - черный• синий магента циан кр•сный магента коричневый белый - cepыii светло-синий - светло-зеленый светлый циан светло-красный светлая магента = желты,й 11рко-белый Рис. 4. 1 . Замена зеленого цветом маrе1па 179 \ . плея, адрес порта которого ЗDАН. Затем нужно послать данные по тому же адресу. ,Чтобы быть уверенным, что массив готов принять номер регистра, а не данные, надо сначала прочитать из. порта ЗDАН, отбросив прочитанное. EGA также использует 16 регистров палитры. Они , расnо-­ ложены в порте с номером ЗСОН, а номера их меняются от 00 до 0FH. Надо сначала .прочитать из порта ЗDАН, чтобы переключить порт на его адресный регистр, затем послать н6мер регистра палитры в ЗСОН, а затем послать данные. Когда переключатели на EGA установлены на улучшенн:{,IЙ режим (для улучшенного цвет­ ного дисплея IВМ), палитра может быть выбрана из 64 цветов. В
180 Глав.i 4 этом случае установка регистра палитры имеет длину 6 бит в фopмaтe-R'G'B'RGB. Биты RGB дают темные цвета, а биты R'G'B' - цвета поВЬiшенной яркости. Когда установлены и R' ,, и R, напри­ мер, это приводит к очень яркому красному цвету. Биты могут смешиваться, давая новые оттенки. Если регистры палитры, пред­ назначенные для 64 цветов, используются не в улучшенном режиме, то 4-й и 5-й биты регистра игнорируются и содержимое регистров рассматривается по обычной схеме RGB. Поскольку PCjr и EGA используют регистры палитры, выбор фонового цвет.\ не ограничен битом 7 байта атрибутов в качестве бита мигания. Монохромные символы Монохромные символы используют байт атрибутов несколько более необычным образом. Как и с атрибу-rами цвета, биты 0-2 уст-.навливают основной цвет, а биты 4-6 - фоновый. Эт1:1 цвета могут быть только белым и черным со следующим соответствием битам: Бит Бит ~ит Лтрнбvт 6или2 5или1 4 I.JJIИ 0 Основной Фо11ш1ый о о о чер11ый чер111,1й о о 1 подчеркнутый белый бет,1й о о белый белый о 1 1 белый белый о о белый .6ею,1й о 1 белый белый о белый белый бе;1ый бс;11,1f1 Нормальный режим - белый на черном, когда биты 0-2 уста­ новлены в 111, а биты 4-6 установлены в ООО. Негативное изобра­ жение создается обратными значениями битов. Символы выводятся с повышенной яркостью, когда бит 3 у.становлен в l; способа при­ дать повышенную яркость фону, когда символы выводятся 8 нега­ тивном изображении, не существует, недоступно и подчеркивание в негативе. Во всех случаях установка в l бита 7 дает мигание символов. Возможны только 10 комбинаций, когда символы видны. Они могут быть реализованы различнь~ми установками битов. Ниже приводятся по одной из возможных установок для каждого случая:
Вывод на терминш~ 181 Атрибут Цеriо•1ка 11\сспш11• 1а - ' }tссити•нюе б~пов . Пl(!И'IIIOC нормальный 00000111 7 7 интенсивный 00001111 1: 15 нормальный подчеркнутый 00000001 1 1 ин rенсивный подчеркнутый 00001001 9 9 негативный 0111_0000 70 112 нормальный мигающий 10000111 87 135 интенсив11ый мигающий 10001111 8F 143 нормальный мигающий подчеркнутый 10000001 81 129 яркий мигающий под•1ерк11утый 10001001 89 137 • яркий негативный 11110000 FO 240 Высокий уровень Бейсик устанавливает цв~;га и атрибуты символов оператором COLOR. Все операторы PRINT и WRIТE, которые следуют за дан­ ным оператором COLOR, выполняются с атрибутами, указанны~и в этом операторе. Цвет фона меняется только для выводимых си.м­ волов, но не для всего экрана. Новый оператор COLOR не влияет на то, что было выведено ранее. За исключением монохромного адаптера, COLOR 3,4 устанавливает основной цвет символа циан <#3), а фоновый - красный (#4). Диапазон кодов основных цветов О - 31, причем числа О -· 15 соответствуют цветам, перечисленным в выше­ приведенной таблице, а числа 16 - 31 получаются прибавлением к любому из этих кодов числа 16, что дает тот же цвет, но с мига­ нием символов. (При мигании основной цвет периодически' меня­ ется на фоновый, в то время как фоновый цвет остается неиз­ менным.) Операторы PRINT и WRITE могут также выводить символы на графический экран. При этом цвет символов - :>то всегда третий цвет текущей палитры', т.е. желтый/коричневый для палитры О и белый для палитры 1. Отметим, что когда вы начинаете работать в цветном тексто­ вом режиме, весь экран черно-белый. Чтобы закрасить весь :>кран в фоновый цвет, необходимо указать оператором COLOR ,2, например, зеленый цвет и затем очистить :>кран командой CLS. Когда вы чистите экран по ходу выполнения программы, необхо­ димо, чтобы последний оператор COL6R установил фоновый цвет таким, каким вы хотите закрасить весь :>кран. Для монохромного дисплея атрибуты vстанав,1иFiаются ·анало­ гичным образом. О соответс:rвуст черному цвету, а ,1юбос из ,,иссл
i82 Глана 4 1 соответствует белому. Таким образом, COLOR 0,7 устанав,1иваст черное изображени~ на белом фоне (негатив), в то время как COLOR 7,0 дает вывод белых символов на черном фоне (обычная vстановка). Имеется одно исключение: сели в качестве основного цвета использовать код 1, то будут •выводип,ся подчеркнутые с11м­ волы. Прибавив 8 к любому из кодов основного цвета, получим яркое изображение. Прибавив 16 к любому из кодов 0-15, полу­ чим мигающие символы. Таким образом, 7 + 8 + 16 = 31 даст яркое мигающее белое изображение. Для фонового цвета допустимы только значения от О до 7. Если вы используете прямое отображение в память \4.3 .1 ), то оператор COLOR не влияет на вывод. Вместо :>того вы должны выбрать требуемую установку атрибутов из таблиц и присвоит~, значение соответствующего байта атрибутов оператором РОКЕ. Помните, что байты атрибутов всегда занимают нечетные позиции в видеобуфере. Отображение в память позволяет иметI, 16 фоновых цветов в Бейсике· (при условии, что вам нс нужны мига­ ющие символы). Для графического адаптера введите. OUT &H3D8,8, чтобы старший бит каждого атрибута действовал как бит яркости для фоновых цветов. В следующем. примере в центре экрана печатается ярко-,красный ··_!" на светло-красном фоне. 100 DШ' SEG = &IIВIIO0 11 О OUT &113D8,8 120 РОКЕ 1000,33 130 РОКЕ 1001,196 ·укаJывасм 1Iа буфер Itнспю1-о :tис1~:1си ·,н·1юл~,зуем 16 фiню111,1х Iщсто11 ·11с 1 н1тасм ! в нс1прс· экра11а ·крас111,1й на сi,етло-красном ( 11000100) Как уже говорилосI,, PCjr хранит бит мигания в масс11ве ворот дисплея. Вот та же программа для PCjr <но она нс будет работап, в режиме двухцветной графики): 100 DEJ0 SEG = &IIВIIO0 110 Х = INl'(&H3ЛI 1) 120 OUT &{13ЛН,3 130 OUT &113ЛН,О 140 РОКЕ 1000,33 150 РОКЕ 1001,196 ·ука:~ывасч на ш1,~с0Gу,tк:р • 1 Iитасм 1-1:\ '\.1НLС1-1Ва нороI ." H ·1Cll.' IC ~I ·требуем ,юпу1I к·реп1с1ру 3 ·сбр::н":ъшасм вес биты :)ТОП) рспtетра '11с 1 Iатасм ! в Itс11трс экрана ·краш1,1й 11а свс·1:ю-крас110"' (1·1000100) Приве;.~_см еще пример изменения назначения цвета регистра ,1алитры. Код цвета, который обычно выводит синий (0001), пуст~, выводит цвет маrента (0101). Номер ·регистра массив.:~ ворот дисплея, соответсtвующий коду цвета 1, - 11 Н. 100 Х = INP (&113ЛШ 110 OUT &113Л11.&1111 120 OUT &113Л11,5 1 11-пасм ~1з :часс~1~а вороI ;11-1сIIлс~I ·требуем ;юl·I у11 к рсI·нсI ру 1111 IIOMelltal''I гу.щ KO. 'l ,1.11·е1II1,I (0101
Вывод иа термииал 183 Средний уровень Прерывания DOS и BIOS предоставляют очень скудные воз­ можности д:ля работы с цветным текстом. Тол1,ко функция 9 пре­ рывания IOH принимает 'байт атрибутов при выводе символа. Функция А прерывания IOH выводит символ без указания цвета или атрибута; она просто помещает символ в видсобуфср, нс трогая байт атрибута, таким образом, атрибуты сохраняют свое старое значение. Функция D прерывания lOH также оставляет нетронутым байт атрибутов. Все :)Ти функции обсуждаются в [4.3. l ]. Функции вывощ1 на :жран DOS прерывания 21 Н всегда выво­ дят белое на черном. Даже сели для всего экрана установлен неко­ торый фоновый цвет, функции DOS устанавливают атрибут в нор­ мальный черный при выводе каждого символа. Одн--.1ко имеется способ преодолеть это ограничение. MS-DOS предоставляет драй­ вер устройства ANSI.SYS, который может интсрпрстировап, специ­ альные Еsс-11оследовательности. В приложскии· Д объясняются основы его использования. Еsс-послсдоватст,ности выводятся через функцию 9 прерывания 21 Н, которая обычно печатает строку символов на экране. В :)Том случае строка состоит из символа Esc, за которым следует 1, а далее одно или более кодовых чисел нз приведенного ниже списка. Строка должна кончап,ся символом m и обычным ограничителем $. Вот кодовые номера: i О 11сс атр11бу·1 ы 111,1к11ючс11ы (•1срный на бс11омl IIKJH011Clla ll()l!l,IIIICIIHa11 flllТCIICНIIIIOCП, 4 IIKJll() 1ICIIO 1юд•1сркн11а11нс 5 IIKЛI0 1 1CIIO Мf11·анис 7 11к11ючс1ю 11сrати111юс изображение 8 IICC IIKJII0 11CIIO (11рн ЭТОМ CHMIIOJll,I IICIIИ}tflMЫ) 30 1 1ср11ый фо11 чср11ый OCIIOIIIIOЙ LLIICT 40 31 крас11ый фон кр11с11ый OCIIOIIIIOЙ IЩСТ 41 32 :1cлc111,1ii фо11 ЗCJICIIЫЙ OCIIOIIIIOЙ l(IICT 42 33 ЖС:П l,lii фо11 ЖСJПЫii OCHOIII ЮЙ ltllCT 43 34 сиш1ii ф011 сиш1ii OCIIOIIIIOЙ ltllCT 44 35 фон ма1'С1па OCIIOIIIIOЙ ltllCT ма1'С1па 45 36 фон 11на11 щ:1юшюii IЩСТ ltflall 46 37 бслыii фо11 белый OCIIOIIIIOii ШIСТ 47 Отмстим, что когда функции MS-DOS выводят символы в гра­ фическом режиме, они обычно используют код 3 текущей палитры. С помощI,ю Еsс-последоватет,ностсй можно установит~,
184 Глава 4 • цвет символа соответствующим любому из цветов па,111тры. Надо указывать 30 или 31 для фонового цвета, 32 или 33 -· для кода 1, 34или35-длякода2и36или37-длякода3.В:ломслучаенс надо указывать фоновый цвет. В следующем примере на экран выводятся две строки с помощью функции 9 прерывания 21Н. Первая выводится синим на красном, а вторая - мигающим цианом на красном. Нс надо пере­ определять красный в качестве фонового цвета для второй строки, поскольку назначения цветов действуют на все последующие команды вывода (включая функции BIOS прерывания IОН), до тех пор, пока не будут сделаны другие назначения. Отмстим, как просто перемешивать команды управления цветом с в1,1водом самих строк. •;---в сегменте данных SТRING I DB 'The rain in Spain',0ЛH,0ПJI:$· STRING_2 DB 'Falls mainly оп the plaiп$· BLUE REDDB 27,'[34;4lm$' BLI CYAN DB 27,'[5;36m$' ;---вывод строк MOV АН,9 ;фу11кцня вывода строки LEA DX,BLUE_RED ;адрес у11рав.r1.~ющей с~роки в DX INT 21Н ;все будет ш,щавап,ся сн1111м на красном LEA DX,STRING_I ;указ1шшеч на пср11ую стро~у INT 21Н ;11счатаем строку l,ЕЛ DX,BLI СУЛN ;адрес ,порой у11ра11л11ющсй строки INT 21Н ;меняем цвет 11:1 мн1·ающ~1ii 11на11 ' LEA DX,STRING 2 ;указ1,шасм на 1п()р'ую строку INT 21Н ;печатаем строку Вы всегда должны заботиться о том, чтобы сбросить атрибуты цвета в нормальное состояние перед завершением программы, поскольку в противном случае они будут действовать и на вв1вод последующих программ. В конце следует вывести Еsс-последова­ тельность, использующую код номер О, как указано выше. PCjr и EGA имеют специальную функцию BIOS для установки сdдержимо[9 регистров палитры. Это подфункция О функции l ОН fiрерывания l0H. Надо поместить номер регистра палитры (от О до 15)вBL,азначениекодацвета(такжеотОдо15)-вВН,а затем выполнить прерывание. Подфункция 2 функции !ОН устанавливает все регистры палитры, а также цвет границы, используя 17-байтовый массив, на который должны указывать ES:DX. Байты 0-15 массива помещаю'l'ся в регистры палитры 0-15,
Вывод на тер.ми11ал 185 а байт 16 устанавли_васт цвет границы. О том, как отдсл1,но установить цвет границы, см. 14.1 .4 1- Низкий уровень Как уже объяснялось в описании примера на Бейсике, надо поместить требуемое значение байта атрибутов в видеобуфер за тем символом, к которому :эти атрибуты должны относиться. При­ ведем лример для цветного адаптера или PCjr. В примере устанав­ ливается текстовый· :>кран 80*25 с 16 фоновыми цветами, а затем экран инициа-!lизируется в красный цвет на свстло-с11нем фоне: ;---устшюпка I Ь фо1юш,1х 1щето11 11 н:кспн;ом режиме II0*25 MOV AI,,OO00HIOI В ' ;ус-IаIю11ка в О бита мнmш111 MOV DX,3O8II ;адрес реI·истра OUT DX,AL ;1юсI,Iщ1ем 11 рсг~sстр ;---инициализируем песь экран п красный 11:1 с11ет1ю-сю1см фоне NEXT: MOV АХ,088001-1 ;указI,шаем на шщеобуфср MOV ES,AX MOV СХ,2000 ;заш1е1,111асм атр~1бут в 2000 11чсек MOV ВХ,1 MOV Al,,I00101008 MOV ES: [ВХ] ,ЛL INC nx INC ВХ LOOP NEXT ;ВХ указывает IIа байт атр~\буто11 ;з1шчс11~н..~ ба{па атр~1(!утов ;носылаем атр11буты 11 буфер ~ущ~;н-1111-шасм указатсл1. 1ra атрибуты ;шII11ем в сле:1ующую rюз~щню 4.1 .4 . Установка цвета rраннц~.1 экрана . Граница символьном экрана может иметь цвет, отличный от фонового цвета центральной насти экрана. Может быть исполь­ зован любой из 16 цветов. С другой стороны, графические экраны технически не имеют области границы. Когда цвет фона устанав­ ливается в графическом режиме, весь экран, включая область гра­ ницы, окрашивается в этот цвет. Однако операции вывода точек ·на экран не имеют доступа к области границы;, если бол1,шую часть ад1:Jесуемых точек экра't!а изменит~, в нсфоновый цвет, то будет создана видимосп, границы экрана. • Высокий уровень Третий параметр оператора Бейсика COLOR устанавливает цвет границы. Используются те же кодовые н_оме'ра цветов, приве­ денные{ в [4.1.З ]. Например, для установки границы в светло-
186 Глава 4 синий цвет надо написат1, COLOR "8. PCjr, кроме того, может изменять цвет за счет изменения установки регистра палитры, соответствующеrо коду цвета, указанного ,:1.ля цвета rран11цы. Полное объяснение см. в 14.1.3 \. Средний уровень Для всех видеосистем фоновый цвет может быть установлен функцией ВН прерывания lOH. Эта функция устанавливает также основные цвета. Чтобы указать, что надо изменит~, фоновый цвет, помещают О в ВН, а код цвета - в BL и выполняют прерывание. Кроме того, PCjr и EGA имеют специальную функцию дмI уста­ новки фоновоrо цвета. Это подфункция I функции I ОН преры­ вания lOH. Надо поместить IOH в АН, 1 в AL и код цвета в ВН. Никаких значений нс возвращается. Низкий уровень Для цветного графического ад~1птсра биты 0-3 порта 3D9H (регистр выбора цвета) устанавливают цвет границы, коrда ::,кран находится в текстовом режиме. Как обычно, назначение битов в восходящем порядке - синий (В), зеленый <Gt, красныii (R) и интенсивность. Поскольку этот _.щрес предназначен тот,ко для записи, все остальные биты этого регистра должны быть правил1,но установлены. Это бит 4, который, ест1 его установит,, в 1, приво­ дит к тому, что вес фоновые цвета будут выводип,ся с высокой интенсивностью . • ; -- -у с1а ~ю вк а CIICTJIO-CflllCI\) l(IICтa грашщы MOV AL,OO00IOOIB ;атрибут с11стло-сн11с,х, ◄ щс·Iа MOV DX,ЗD9ll ;адрес рс,·~~стра 11ыбора ц11ста OUT DX,AL ;усташ111.:1н11асм It11ст I·ра1IнItы Для PCjr массив ворот дисплея 14.1 .1) имеет регистр, который устанавливает цвет границы. Это 4-битовый регистр, причем биты 0-3 соответствуют синему, зеленому, красному и высо~zой интен­ сивности, когда установлены в 1. Для установки ·свстло-синсrо цвета надо послать в регистр l 001. Регистр цвета границы - жо регистр 2 массива ворот дисплея. Чтобы получит,, доступ к :JТому регистру, надо сначала послать 2 ~ порт по адресу ЗDАН. Затем надо послать данные по тому же адресу. Чтобы быть уверенным, что микросхема готова принять номер регистра, а нс данные, надо
Вывод 11а тер.мш1ал 187 сначала прочитап, из порта ЗDАН. Следуюш1iй пр11мср устанав­ ,н1васт красный цвет границы <61п 2 установ,,сн): моv IN моv OUT моv OUT l)X,Зl)ЛI 1 Лl,,l)X Лl"2 DX,ЛL, AI,,4 DX,ЛL ~ a:1pt.:c ll<)JП(I .\.1a((Иlta IIOJ)<H ,1ис11.:1сs.1 ~ЧTt.:IIИC }J,ЛИ 110,1 \l'PTOBJ.:И MИKf}()(Xl;Ml,I ;1юмср трсбусмоl'О rс1:истра ;l1осыласм в 11ор1 ;уста11авлинасм тол1,ко бит 2 ;ус! ,;illaBJIИIШCM ,l ~BCT 1·ра11и1~1.1 Для EGA цвет границы устанавливается регистром сканирr:--. вания (ovcrscan). Это регистр номер 11 Н порта с адресом ЗСОН. Надо сначала прочитать из :~того порта, чтобы переключит~, cro на адресный регистр, затем послап, туда номер 11 Н в . качестве индекса, а затем послать данные. Имеют значение тол1,ко младшие 4 бита данных, сели только EGA нс связан с улучшенным цветным дисплеем IBM, а в :лом слvчас имеют значение младшие 6 бит, которые устанавливают цвет ~раницы. 4.1.S. Очистка частн/всеrо экрана Очистка экрана состоит в записи пробела в каждую из позищ1и экрана (код ASCII - 32). Однако сели при выводе на :~кран были 1 использован~• нснормат,ныс атрибуты, то должны быт1, такж~ изменены и байты атрибутов. Операционная система обеспечивает простой способ очистки тол1,ко части :~крана. Высокий уровень В Бейсике для очистки экрана применяется оператор CLS. При этом 25-я строка внизу экрана становится- пустой, только если был убран список значений функциональных клавиш с помощью команды КЕУ OFF. Байты атрибутов устанавливаются равными АSСП 7. В [4.5 .l] дана процедура прокрутки, которая в Бейсике служит для очистки окон на экране. Средний уровень Операционная система предоставляет несколько способов очистки :~крана. Ваш выбор зависит от того, какие средства требу­ ются программе для достижения других целей. Первый метод :~то просто сброс режима дисплея с помощью функции О прерывания l0H [4.l.2 ]. Для символьного :~крана каждая позиция заполняетсs1
188 Глава 4 пробелом (ASCII 32), а все атрибуты устанавливаются нормаль­ ными (ASCII 7). Обычно этот метод хорощ только в начале прог­ раммы, ко~да все равно надо устанавливать режим работы дисплея. •Для цветного графического адаптера и PCjr реинициализация режима дисплея приводит к катавасии на экране. Этот эффект отсутствует у монохромного адаптера и f:GA. ;---очистка экрана путем установки нового режима MOV АН.О ;11омер фу11кцш1 установк~1 режима щIспле11 MOV AL,2 INT !ОН ;код режима 80*25 •1ер110-бслого ;очистка экрана Второй метод состоит в использовании функций 6 и 7 прерыва­ ния l0H, которые сдвигают экран. Число строк, на которое надо сдвинуть экран, помещается в AL, и когда это число равно нулю, экран очищается. Прерывание позволяет сдвигать только часть экрана, поэтому таким образом можно очистить отдельное окно на экране. Надо поместить координаты левого верхнего угла окна в сх; а координаты правого нижнего угла в DX (номер строки в CH/DH, а номер столбца в CL/DL). Поместите атрибут, с которым должен чиститься экран, в ВН. Координаты отсчиты­ ваются от О. \ ;---оч~1стка окна между 3.4 и 13. 15 MOV АН,6 ;используем процедуру сд11ша MOV AL,O ;число строк сдI1ш11 делаем равным нулю MOV ВН,7 ;байт атрибутоu для заполнения , MOV СН;З ;строка для оерхнеl'о лenoro угла моv CL,4 ;столбец для ле1юrо uерхнеIх, угла MOV DH,13 ;строка для шIжI1еrо левого угла MOV DL,15 ;столбе1t д.1111 нижIIеIх, лс1к1rо уI·;ш INT !ОН ;ч~ICT~IM ОКНО Третий метрд заключается в использовании функции 9 преры­ вания l0H, которая выводит символ и атрибуты стол1,ко раз, сколько указано в СХ. Значение 2000 чистит весь :жран, если кур­ сор был установлен в 0,0 методом, показанным в [4.2 .l ). АН дол­ жен содержать символ пробела, AL - байт атрибутов, а ВН - номер страницы дисплея. ;---установка курсора в левый верхний угод экрана MOV АН,2 ;функция уста~юI1ки курсора
вьiвод на терминал моv вн.о моv ох.о ;номер страшщы ;координаты о;о INT !ОН ;уста11анлипаем курсор ;---вывод символа пробела 2000 раз MOV АН, 9 ;,юмер функции MOV СХ,2000 ;•шсло повторений вывода •MOV AL,' • ;символ пробела в AL MOV BL,7 ;атрибуты в BL INT !ОН ;очистка экршш 189 Наконец, DOS обеспечивает очистку экрана с помощью специ­ альных Еsс-последовательностей, которые работают с драйвером ANSI.SYS. Основные сведения о нем приведены в приложении Д. Эти последовательности - строки, начинающиеся с символа Esc, а завершающиеся ограничителем $. Такие строки выводятся функ­ цией 9 прерывания 21 Н, при этом DS:DX должJ::!Ы указывать на первый символ строки. DOS интерпретирует строку, не выводя се на дисплей. Чтобы стереть всс1, экран, строка должн,t быть 12J. Чтобы стереть конец строки, начиная от позиции курсора (вклю­ чая эту позицию), строка должна быть [К. ;---в сегменте данных CLE_LIN DB 27,'[К$" ;---очистка конца строки от позицш1 курсора MOV АН,9 ;фу11кцш1 111,р~ода строки LEA OX,CLE_LIN ;ОХ должсII указI,111ап, на 11а"ало строки INT 21Н ;сп1расм кmIсц. строки Низкий уровень На низком уровне надо просто поместить символы ·пробела и требуемый байт атрибутов в память дисплея, используя инст­ рукцию STOSW. Вот пример для монохромноrо дисплея: моv· АХ,ОВОООН ;указыв.'\ем Iш r1амять дисr1лея MOV ES,AX MOV DI,0 ;О\ уkаз1,111аст Iш 1щ•11uю буфера MOV AL,32 ;симIк,л 11робсла MOV АН,7 ;11ормалы1ыс атрибуты MOV СХ,2000 ;'lисjю IIOIIТ0J)CШl~i .. REP sтosw ;1юсыласм АХ 11 ES:1)1 20(~1 раз
190 Глава 4 4.1 .6 . Переключение между видеоадаптерами Машина может быть оснащена II монохромным, 11 цветным адаптером или одним из :1Т11х адаптеров и EGA. Программа может выбирать, какой из МОРИТОр<)Р должен fытt, активным, и оменяя значения битов 4 и 5 в ячейке памяти 0000:041 О. Установив оба бита в. 1, мы выбираем монохромный адаптер. Изменив установку битов 5-4 на 10, устанавливаем графический адаптер в режиме 80 символов в строке, а на 01 - 40 символов в строке. И наконец, изменив биты на 00, выбираем EGA. Во всех случаях вы должны немедленно подать команду установки режима, поскол1,ку BIOS имеет еще очень много рсrистrон, которые чадо изменить, прежде чем дисплей будет работать нормально. Отметим, что хотя операционная система не может управлят1, одновременно двумя мониторами, программы могут осуществлят~, вывод на оба дисплея, используя прямое отображение в памят~, [4.3 .1 ] для адресов буфера неактивного монитора. Высокий уровень В Бейсике надо применит~, следующий код: 100 Тlсрсклю•1сш1с Щ\ MOIIOX\IOMlll,IЙ ;1~1c11;16i 1 110 КЕУ ОН': CLS 120 WIDTJI 40 130DEFSEG=О 140 М = РЕЕК(&Н410) 150 J'OKE &Н410,М OR &1130. 160 WIDTH 80 170 LОСЛТЕ"1,12,13 11\0 КЕУ ON 100 •11срсклю•1сщ1с IШ. ЦIICТIIOfi 1·paфH'ICCкi1ii ;1нс11лсii (ХО СНМВОЛОВ} 110 КЕУ 01'1': CLS 120 WIDTH 80 130DEI'SEG = О 140 М = РЕЕК(&Н410) 150 РОКЕ &11410,(М ЛND &IICI') OR &1120 160 WIDТII 80 170 SCREEN О 180 ШСЛТЕJ,6,7
Вывод па терминал. 190KEYON 100 Лерсклю 11е1ше на EGA (80 с•1мволоu) 11 О КЕУ OFF: CLS 120 WIDTH 80 130DEFSEG=О 140 М = PEEK(&ll410) 150 РОКЕ &Н410,М AND &Нс1: 160 WIDTH 80 170 SCREEN О 180 LOCATE"1 ,6,7 190KEYON 191 Измените команды WIDTH и SCREEN, чтобы переключиться на другие начальные режимы дисплея. Низкий уровень В ассемблере, как и в Бейсике, надо непосредственно изменит~, биты 4 и 5 по адресу 0000:041 О и сбросить режим дисплея сразу ·вслед за изменением. ;---11срсклю•1сш1е 1ш мо11охромш,1й монитор SUB ЛХ,ЛХ ;об11уJ111см Л:Х MOV ЕS,ЛХ ;~•сп1ш111лиш1см ES на Шl'ШJЮ н:1м11п1 MOV Dl,,ES:1410111 ;,юлу•шем байт 1ю :щресу 0000:0410 OR DL,001 I0000B MOV ES:[41011],Dl, . MOV Лll,0 MOV _Лl,,О INT 1011 ,5·сп1н:111J1ищ1см бю·,~ 4 и 5 ;щ>звра11~асм б.ай1· ;фу11к~1ш1 уста~ю11кн режима днс11лсs1 ;мо1юхромныii режим 80*25 ;уста11а11л~111асм режим ;---нсрсключснне на 1111спюй мош~тор (40 01м1ю;юu) sun ЛХ,ЛХ ;усп11~'а11ли11аем ES на 11а•~ало 1шмs1п1 MOV 'ЕS,ЛХ MOV DL,ES:141011] ;берем байт 110 а11ресу 0000:0410 ЛЮ) 01,,1100111 IВ OR Dl,,000IOOOOB M()V ES:(41011],Dl, MOV Лll,0 ;сбрасы1~..'1ем б~п,,1 4 и 5 ;ус·rа1rавливасм б1-1т 4 ;11оз11ращасм байт ;функ11ия устnно11к~, режима 11нс11ле11
192 MOV AL,l INT !ОН ;цветной режим 40*25 ;уста11а11лш~ае"'I реж~1м ;---переключение на EGA sun АХ.АХ моv ES,AX MOV , DL,ES: [410Н] AND DL,11001111B MOV ES: [410Н] ,DL MOV АН,О MOV AL,1 INT !ОН ;уставашн-шасм f:S на 1ш11ало 11ам~пи ;берем бМп 1ю адресу 0000:0410 ;сбрасываем бтъ1 4 и 5 ;возвращаем байт ;функция установки режима дисплеи ;цветной режим 40*25 ;устанаил~шаем режим 4.2. Управление курсором 1 Глана 4 Курсор служит двум целям. Во-первых, он служит указателем места на экране, в которое операторы прог­ раммы посылают свой вывод. Во-вторых, он обеспечивает видимую точку отсчета на экране для пользователя программы.' Только ,1ля второго применения курсор должен быть видимым. Когда курсор невидим (выключен), он все равно указывает на позицию экр,ша. Это важно, поскольку любой вывод на экран, поддерживаемый операционной системой, начинается с текущей позиции курсора. Курсор генерируется микросхемой контроллера диспле\1 6845, описанной в [4.1 .1 ]. Э;га микросхема имеет регистры, устанаР­ ливающие размер и положение курсора. Микросхема fl845 обеспе­ чивает только мерцающий курсор, хотя имеются программные спо­ собы создания немерцающего курсора [4.2 .6 \ . Частота мерцания курсора не может быть изменена. В графических режимах курсор не выводится, хотя символы позиционируюто1 на ::>кране теми же процедурами установки курсора, что и в текстовых режимах. Когда видеосистема работает в режиме, допускающ.:м нес­ колько дисплейных страниц, каждая страница имеет своi1 собст­ венный курсор и при переключении между страницами восстанав­ ливается позиция курсора, которую он занимал, когда было пос­ леднее обращение к восстанавливаемой странице. Некоторые режимы дисплея поз1юляют иметь до 8 дисплсйн1.~х страниц и соответствующие им позиции курсора хранятся н н.1боре восьми 2-байтовых переменных в области данных BIOS, начиная с адреса 0040:00S0H. В каждой переменной младший баiiт со,'1Ц,ж11т номер
Вы.вод 11а тер.мииал 193 столбца, отсчитывая от О, а старший байт содержит номер строки, также отсчитывая от О. Коrда число страниц мен1,ше 8, используются переменные, расположенные в более мж1дш11х адресах памяти. 4.2 .1. Установка курсора в абсолютную позицию Для курсора моrут быть установлены абсолютные координаты или координаты относительно ero текущей позиции 14.2 .21 . Абсо­ лютные координаты моrут меняться в прсдс11ах 25 строк 11 80 (иногда 40) столбцов. В языках высокоrо уровня обычно отсчи­ :гывают координаты экрана начиная с 1, и, таким обр,tзом, пози­ ция левого верхнеrо угла - J, 1. В ассемблере отсчет всегда начи­ нается с нуля и позиция лсвоrо всрхнсrо угла - 0,0. Высокий уровень , Бейсик нумерует строки от 1 до 25, а столбμы - от I до 80. Формат оператора LOCA ТЕ, который устанавливает позицию кур­ сора, такой: LOCATE строка,столбец. Если курсор нс устанав­ ливается, то он переходит в первvю позицию строки после ввода возврата каретки, а сдвиr экрана ~дчинается после тоrо, каR будет заполнена 24-я строка. Чтобы вывести в 25-ю строку, ~ы должны использовать LOCA ТЕ (предв.tритсльно очистив эту строку с помощью КЕУ OFF). Для отмены автоматичсскоrо сдвиrа экрана в строках 24 и 25 надо завершать оператор• PRINT точкой с запятой (чтобы отменить сдвиr в позищ1ях 24,80 и 25,80, надо использовать прямое отображение в память (4.3 .1 ]). Ниже приведен пример рисования вертикальной черты с помощ1,ю одноrо -из символов псевдографики в центре экрана: 100FORN=1ТО25 110 LOCATE N,40 120 PRINT CHR$(186); 130 NEXT rюuтор для кnжJ1of, строки устшю11ка курсора II ссрс11н"у строки ·11счатасм 11ср·1 ~-1калы1у10 1 1срту ·нсрсход к сленующсй строrс Коrда используется несколько дисплейных страниц, оператор LOCATE действует на текущей активно1i странице памяти. Если страница, выводимая в данный момент на монитор, не активна, то положение курсора на экране не меняется. Отметим, что Бейсик имеет собственную переменную, хр..1нящую текущее положение курсора. Если вы подключите ассемблерную nодпроrрамму, кото- 7 Р. джордейн
194 Глава 4 рая изменит положение курсора, то Бейсик проиrнорирует новую позицию курсора, когда ему будет возвр.1щено управление. Средний уровень Операционная система предоставляет два способа позищюни­ рования курсора в абсолютную позицию на :>кране. Функция 2 прерывания lOH устанавливает курсор, относящийся к указанной странице памяти. Страницы нумеруются начиная с нуля и длs~ монохромного АИСПлея номер страницы (йаходящийся в ВН) должен всегда быть равным О. DH:DL содержат строку и столбец, которые тоже нумеруются. с О. Курсор меняет свое положение на экране, только если установка курсора относится к текущей активной странице. ;---установка кypcopii в строку 1З, столбец 39 MOV АН,2 ;номер функции MOV nн,о ;номер стршшцы моv DH,13 ;строка MOV DL,39 ;столбец INT IOH ;позиционируем курсор Второй метод позиционирования курсора состоит в применении специального драйвера устройства ANSI.SYS, который должен быть ' загружен при старте системы. В приложении Д даны необiодимые сведения. Для посылки команды, содержащей информацию о строке и столбце, служит функция 9 прерывания 21Н. Строка начинается с символа Esc (ASCII 27), а завершается символом-ограничителем $. Формат строки Esc [строка, столбецН$, где строка и столбец нумеруются от нуля, а Esc обозначает код ASCII 27. Например, строка 27,'10;60Н$' устанавливает курсор в строку 1О, столбец 60. Хотя такой метод кажется излишне сложным, он оказывается очень удобным при выводе ряда строк на экран, так как Еsс­ последоsательность обрабатывается как одна из строк набора. В данном примере три строки сообщения разбросаны по всему экрану. ;---в сегменте данных POS_I DB 27,'[10;ЗОН$' STR_ 1 DB 'Тhere are two options:$· POS_2 on 27,'[IЗ;32Н$' STR 2 DB ·ш Review part 1$' РОS_З DB 27;'(15;32Н$'
Выаод 11а тер.ми11ал 195 STR_З DB '(2) Movc orI 10 ра,·1 2s;· ;---IIе•шть с,·рок. MOV Лll,9 ША DX,POS- INT 2111 ША DX.STR 1 - INT 2111 ША DX,POS_2 INT 2Ш ША DX,STR_2 INT 2111 LЕЛ DX,POS_З INT 2111 1,ЕЛ DX,STR_З INT 2111 Низкий уро11ень ;11омср фу11кнш1 111,1110;щ строки ; 1-w строка 11оз,II1,юшI1ющI11н11 курсора ;IIоз,щ,ю11ирусм курсор ; 1-я -. скс I ощ1и строка ;вi.iвo;t стр<>к1-1 ;,1 т.д. Регистры 14 и 15 микросхемы 6845 хранят положение курсора. Вы можете изменить их значение и курсор передвинется в соответ­ ствующую позицию экрана, но прерывания вывода на :жран DOS и BIOS будут игнорировать вашу установку и вернут курсор в старое положение. Это происходит потому, что каждый раз при вызове :>тих прерываний они восстанавливают регистры курсора, используя 2-байтовос значение, хранящееся в области данных ВIOS. В этой области, начиная с адреса 0040:0050, могут нахо­ диться до в"сьми таких значений, дающих текvщес положение курсора для каждой из страниц дисплея. Процедура низкого уровня должна модифицировап, и эти значения, чтобы ИЗМ<;JiИТь сос1оянис курсора полност1,ю. Позиция курсора хранится в регистрах 14 и 15 как чщ:ло от О до 1999, что соответствует 2000 <25х80) позициям экрана. Нс спу­ тайте эту систему нумерации с позициями видсобуфера от О до 3999, где каждый символ сопровождается еще байтом атрибутов (для получения эквивалентного указателя на позицию курсора надо сдвинуть указатель видсобуфера на 1 бит вправо). Обращаем также ваше внимание на то, что нс надо менять местами старший и младший байты: в регистре 14 - старший, а 15 - младший. 7* ;---в 11роI·рамме MOV BL,24 MOV 811,79 ;строка 11 111, (0-24) ;столбе,~ 11 1111 <О- 79) CЛLL SET CUR ;вызов 11роцсдуры ;---11роцедура уста11овки курсора
196 Глава 4 SET_CUR PROC ;полуqаем доступ к регистру младшего байта MOV DX,3B4H • ;порт адресного регистра 6845 MOV AL,15 OUT DX,AL ;выбираем регистр 15 ;посылаем запрос ;вычисление позиции курсора MOV AL,80 ;умножаем номер строки на 80 MUL BL MOV BL,BH SUB ВН,ВН ADD АХ,ВХ ;в АХ - номер с'rроки, ум11оженный на 80 ;переносим номер столбца в BL ;распространяем BL на ВХ ;вычисляем позицию курсора ;посылаем младший байт результата INC DX ;адресуем увравляющ~1й регистр OUT DX,AL ;nосылаем младший байт ;получаем доступ к регистру старшего байта MOV AL,14 ;номер требуемого регистра DEC DX ;восстанавливаем порт адрес1ю1'0 регистра OUT DX,AL ;посылаем запрос , ;посылаем старший байт результата INC DX MOV AL,AH OUT DX,AL RET SET CUR ENDP ;адресуем управляющий реп1стр 1 ;помещаем старш~1й байт 11 ЛL ;посылаем старuтй байт 4.2.2 . Относительное позиционирование курсора \ Иногда полезно сдвинуть курсор относительно его предыдущей позиции: на строку вверх, на три столбца вправо и т.д. Достаточно просто использовать для этой цели уже описанное абсолютное позиционирование курсора. Но для удобства MS-DOS предостав­ ляет некоторые возможности относительного перемещения курсора. u ! Среднии уровень Функции относительного перемещения курсора выполняются Еsс-последовательностям.и. Это строки, которые ВЬIВОдятся на экран с помощью функции 9 прерывания 2IH. В приложении Д' даны ~основы их использования. Такие последовательности интер­ претируются MS~DOS как команды перемещения курсора, а нс вы-
Вывод на терминал 197 ! вод символов строки. Строка начинается с символа Esc (ASCII 27), затем идет символ [·, а символ $ .отмечает конец строки. Сама строка состоит из числа позиций, на которое надо сдвинуться, -11 кода направления. Сдвиг на 3 позиции: вверх ЗА 111\ИЗ 38 апраао зс IIЛeJJO 3D • Числа записываются как коды ASCII. Не преобразуйте, напри­ мер, ЗЗС (33 пробела вправо) в 33,'С'; должно быть 'ЗЗС'. В приведенном примере цифры 1-8 помещаются через определенные интервалы поперек э~рана, как метки .столбцов данных. Проме­ жутки между цифрами генерируются Еsс-последовательностями, которые сдвигают курсор вправо после вывода каждой цифры. ;---в сегменте данных CUR_RITDB 27,'(9С$' ;---установка начальной 11оаицш1 курсора MOV ВН,О ;номер страницы MOV DH, 1 ;строка MOV DL,5 ;столбец MOV АН,2 ;функция уста~ювкн курсора INT lOH ;уста1ювка курсора ;---IIЫIIOД цифр LEA BX,CUR_RIT ;ВХ будет обмешша;r1.ся с DX MOV СХ,8 ;•~ис;ю цифр дли вьшода MOV DL;o· NEXT: MOV АН,2 INT 211-1 INC DL XCHGDX,BX моv АН,9 INT 21Н XCHGDX,BX LOOPNEXT ;1~а•ш11аем с О ;фу11кнт1 DOS д;111 111,шода с~1м1юла ;11ь111однм CHMIIOЛ ;персх1щ~1м к следующему коду ЛSCII :r~омсщасм указатет, 1111 с1·1)0ку 11 DX ;фу11кц~1я 111,1uощ1 строки ;сдвигаем курсор на 9 1юзици•i 11111»ню ;возвращаем в DX код ASCII ;переходим к следующей цифре Существует также пара Еsс-последовательностей, которые управляют переносом курсора на следующую строку при дости­ жении им конца текущей строки. ~когда устанавливается отсут­ ствие переноса, лишние символы при выводе отбрасываютсs~. Строка, запрещающая перенос, Esc [ = 7h (27,' 1= 7h ·) . Для· воз­ врата к режиму автом~tтического переноса на tледующую строку • исцользуется строка Esc [ .. 71 <27,' 1= 71').
198 Глава 4 4.1.3. Включение н выкnюченне курсора Курсор генерируется микросхемой 6845. Он функционирует совершенно независимо от видеопамяти. Это знач1tт, что при прямой адресации в память дисплея [4.3 .1 J программное обеспе­ чение должно координировать перемещения курсора с вставкой нового символа в буфер. Отметим, что микросхема 6845 не может ни создавать немерцающий курсор, ни изменять частоту его мерцания. В (4.2 .6] показано, как ~конструировать другие "искус­ ственные" типы курсора. Высокий уровень -Интерпретатор Бейсика автоматически выключает курсор пр~1 запуске программы: Курсор появляется, когда испол1,зустся опера­ тор INPUT. Если вашей программе необходим курсор, скажем для процедуры INKEY$, то он должен быть включен установкой треп,­ еrо параметра оператора LOCATE в 1 (0 снова выключит его). Напоминаем, что первые два параметра оператора LOCA ТЕ уста­ навливают строку и столбец, в которых должен выводиться курсор. 100 LOCATE 15,40,1 100 LOCATE "1 100 LOCATE "0 ;нклю•шп, курсор, CIX) ll!)З~ЩИW 15,40 ИШI ;вклю•шп, курсор 11 текущей 1юз~щ~1и ~• ;с1101щ lll,IKJII0 1 1ИTI, курсор Курсор будет оставаться при последующих появлениях опера­ тора LOCATE без установки каждый раз трст1,еrо параметра. Однако надо отметить, что операторы INPUT и INPUT$ выклю•1а:г . его после их выполнения. Средний уровень Ассемблерные программы оставляют курсор включснж,1м до тех пор, пока им не rказано обратное. Операционная система нс предоставляет специальных средств выключения курсора, но ::rro легко сделать. Надо просто позиционироват1, курсор за прсдет,1 ЭKJktнa, с помощью функции 2 прерывания I ОН установит~, ег9 /в первую позицию 26-й строки. Помните, что координаты отсч1t­ тываются от нуля, так что ::>той позиции соответствуют коор­ динаты 25,0. MOV RH,O MOV DH,25 ;1юмср страшщ1,1 (11сс1·на О д.ня мо1юхром1ю11,) ;строка
ВЫ:вод 11а тер.мииал MOV DL,0 MOV АН,2 JNT !ОН Низкий уровень 199 ;столбец ;номер фу11к1tш1 ;уста~tа11JIннnсм курсор за IIрсщ:лI,I :жра11а Бит 6 регистра 10 микросхемы 6845 [4.l .l] выключает курсор, когда он установлен в 1, и включает его, когда сброшен в О. Этот регистр содержит также значение "начальной строки" для курсора, которое вместе со значением "конечной строки" определяет тол­ щину курсора (4.2.4 ]. Поскольку тип курсора не имеет значения, когда курсор выключен, надо просто поместить в регистр I О значе­ ние 32, чтобы установить бит 6. Для восстановлСliия курсора вы должны также вернуть значение "начальной строки" курсора. В обычном случае это значение равно 11. Значение "конечной строки" при этих процедурах не меняется, поскольку рно хранится в другом регистре. ;---111,Iклю 1 1е111Iе курсора • MOV DX,38411 MOV Л!,,10 OUT DX,AL INC DX MOV ЛL,32 OUT DX,AL ;1юмщ> 11орта а;1ресIюI1J рсI·1~стра 61!45 ;11ы(юр (>Cl'f1eтpa 10 ;Iюс~.1лаем зшI1юс ;ж,сту11 к рсI·нстру через с;1с11ующ~1й Iюрт ;усп111а11лш1асм бит 6 1йtи I1ык;Iю•IеIIн>1 курсора ;I11.Iклю•шсм курсор ;---обрапюе включешIс курсора MOV AL,11 OUT DX,AL ;з1~ачс111-1с ··1ш 1 ш;1ы1ой строки·· ;11ключасм курсор 4.1.4. Иэмененне формы курсора Курсор может менятI,ся по толщине от тонкой линии до макси­ мального размера, о,тводимого под символ. Он строится из корот­ ких горизонтальных отрезков, верхний из которых называется 1tа 1шлышii строкой курсора, а нижний - ко11еч11ой строкой. Для монохромного дисплея под каждый символ отводится 14 строк, пронумерованных от О до 13 начиная сверху. Промежутки между символами обеспечивают~я двумя верхними строками и тремя нижними. Большинство символов располагаются в строках 2-1 О, хотя хвостики некоторых символов достиrают линий 12 ~, 13, в то ·время как подчеркивание занимает одну двенадцатую строку. На 200-строчном цвст!iОМ дисплее для каждоrо символа отвс­ дится только 8 строк, а симIюл рисуется в верхних семи строках.
200 Глав,i 4 Эти 8 строк пронумерованы от О до 7 начиная сверху, и нормал1,­ ный курсор формируется одной строкой 7. (Отмстим, что на цвет­ ном дисплее нет подчеркивания, поскольку испол~,зование д,1я этого строки 7 привс,10 бы к тому, что символы с1ивались бы с расположенными под н·ими.) Цветной дисплей высокоrо разре­ шения использует 14-строчный монохромный вариант, коrда он работает в режиме высокого разрешения, и 8-строчный вариант при работе в одном из цветных rрафических режимов. Курсор может быть сформирован любой комбинацией прилеrа­ ющих отрезков. Для монохромного дисплея он занимает вес отве­ денное под символ место, когда начальная строка равна О, а конечная строка равна 13 (для графического дисплея значение конечной строки должно быть равно 7). Если значения начальной и конечной строк совпадают, то возникает однострочный· курсор. Если номер конечной строки меньше, чем номер начал1,ной, то возникает курсор, состоящий из двух частей, так как происходит перенос в верхние строки. Например, если начальная строка равна 12, а конечная - 1, то снача.1а заполняется строка 12, затем - 13, затем - О и, наконец, - l. Курсор при этом принимает форму двух параллельных линий, указывающих верхнюю и нижнюю rраницы ряда, который он занимает. BIOS хранит 2-байтовую переменную по адресу ()040:0060, '\.. ., .. которая содержит текущие значения начальнои и консчнои строк. Первый байт содержит значение конечной строки, а второй - начальной. Высокий уровень В Бейсике оператор LOCA ТЕ может нс тол~,ко позици­ онировать курсор и включать или выключат~, его, но и управлят1, его формой. Параметры, устанавливающие начальную и конечную строки, - это 4-е и 5-с числа, следующие за словом LOCA ТЕ. Дру­ гие параметры могут быть опущены, если присутствуют разделя­ ющие их запятые. Таким образом, чтобы создать толстый курсор, занимающий строки со 2 по 12, надо записать LOCATE ,,,2,12. Отметим, что Бейсик обычно выключает курсор, когда начинает выполнение nрограммы. Как включить его см. в [4.2.3 ]. Средний уровень / Функция 1 прерывания BIOS !ОН устанавливает начальную и конечную строки курсора. В СН должна быть указана начальная, а в CL - конечная строки.
Вывод на терминал 201 ;---установка начальной fl коне•нюй строк курсора MOV АН, 1 ;1юмер фу11к1tш1 MOV СН,О ;1ш•1ап, курсор 11 11срх11ей строке MOV Cl,,7 :оv.011•:ип, курсор в 11ос1,мой ороке INT IOH Низкий уровень Регистры 10 и 11 контроллера дисплея 6845 содержат з11аче1. ия начальной и конечной строк соответственно. Доступ к обоим регистрам осуществляется через порт 3В5Н. для ~онохромноrо адаптера и через порт ЗDSH - для цветного адаптера и PCjr. Пред­ варительно надо послать номер требуемого регистра в адресный регистр, имеющий адрес порта 3В4Н (см. (4.1 .1 ]). Значения зани­ мают младший конец каждого регистра. Однако регистр начальной строки ( # l 0) • битами 5 и 6 указывает также, должен ли выводиться курсор: Курсор выводится, когда оба эти бита сбро~ шены в О. Поместив в регистр только номер начальной строки, мы установим эти биты в О. Остальные биты этого регистра нс исполь­ зуются. ;---установка 1ш•шлыюй строкf1 MOV DX,38411 ;достун к адрес1юму рt:п1стру 6845 MOV AL,10 ;выбор рс1'f1Стра 6845 OUT DX,AL ;посылка запроса MOV AL,O ;номер 1ш•1ш1ыюii строки О INC DX ;11срехо;1им к управляющему реп1стру OUT DX,AL ;rюсылаем 1юмер началыюй стр~Жf1 ;- --установка "конечной строки" MOV AL, 11 ;выбираем ре1·f1Стр 11 'DEC DX OUT DX,AL MOV AL,7 INC DX OUT DX,AL ;11озпращаемс11 к адресному ~е•·истру ;посылаем запрос ;номер ко11с•111ой строки 7 ;11срсходf1м к управляющему регистру ;11ос~.1ласм 1юмер ко11ечной строкf1 4.1 .S . Чтенне/сохраненне/восстановnенне познцнн курсора Программы иногда читают и сохраняют текущее положение курсора, с тем чтобы можно было временно перевести курсор в командную строку, а затем вернуть его в исходную позицию. Текущая позиция курсора для каждой из доступных страниц .хра­ нится в области данных BIOS. Имеется восемь 2-байтовых пере­ менных, размещающихся начиная с адреса 0040:0050. Первая
202 г,,ава 4 позиция соответствует странице О, вторая - стр,1ницс I и т.д. Младший байт каждой переменной содержит номер сто,1бца, а старший - номер строки. Как столбцы, так и строки нумеруются начиная с нуля. Высокий уровень в· Бейсике оператор CRSLIN возвращает строку, а Pos· - столбец. Оператор POS должен быть снабжен фиктивным аргу­ ментом, т.е. он всегда должен записываться в виде POS<0). В дан­ ном примере курсор переводится в нижнюю строку экрана, а затем возвращается на место. Отмстим, что курсор возвращается на место после выполнения оператора INPUT [4.2.З ]. 100 ROW = CRSIJN 1юлу 1 шсм строку курсора l IО COL = POS<0) ·поJ1у11асм стодбс11 курсора 120 LOCATE 25,1 • 11ерс1юднм курсор 11 кома111111ую строку 130 INPUT "Entcr filc namc" . 1 '$ за11рос щ1 1111rщ 140 l"ОСЛТЕ ROW ,COI" 1 ·11осста~1а11щ1щ1см llfФ1щ110 куро1ра Средний уронень Функция З прерывания I0H возвращает строку курсора в DH, а столбец - в DL. На входе надо поместит~, в ВН номер страницы (всегда О для монохромного дисплея). ;---011ределение познцш1 курсора MOV ЛН,3 ;1юмер фу11кц~1н MOV ВН,О ;страница О INT I0H ;строка:стодбсц 11 DH:DI, MS-DOS предоставляет две Еsс-послсдоватст,ности д,,я сохра­ нения и восстановления позиции курсора. Это спецщ1л1,ныс строки, которые, если их "вы11ести" на терминал, упра1тяют монитором. Основы использов.шия этих последовательностей опи­ саны в приложении Д. Последов~1тсльность для запоминания пози­ ции курсора - Esc\s, а для восстановления - Esc[u. Нет нужды запоминать координаты в переменной. ;---в сегменте данных SAV_CUR DB 27 ,' [s$' RES_CUR DB 27 ,' [11$' ;---сохране11ие курсора ;сохр1111с11ие поз~щщ1 курсорн ;1юccтatl()IIJICШIC IЮЗ~ЩШI курсора LEA DX,SЛV_CUR ;апрсс Ш\IШJII\ строки II ох
Вывод ua терминал MOV All,9 INT 2111 ;---11осста11011лсш1с курсора \,ЕЛ OX,RES_CLR MOV All,9 INT 2111 Низкий уровень ;,юмср фу11к11ш1 ш,11ю;щ строки ;сохра1111см 11озинню курсора ;a;1pcc·11a'laJ1a с1рок11 в fJX ;1юмср фу11к11ш1 111,1110:щ строки ~восс1а~1авлнвасм 1юзн1н~"10 курсора 203 Регистры 14 и 15 микросхемы 6845 хранят текущую позицию курсора, как объяснялось в [ 4.1.1 1. Старший байт хранится в регистре 14. Два байта хранят числа от О до 1999 в режиме 80 символов в строке и от О до 999 в режиме 40 символов. Вам необ­ ходимо перевести получаемое число в координаты строки и столбца. Вы можете прочитать :Jто значение, чтобы узнать теку­ щую позицию видимого курсора на экране. Но запоминание :Jтого значения и последующее восстановление его в регистрах нс обяза­ тельно приведет к возврату курсора в предыдущую позицию, осо­ бенно если ваша -программа испоm,зуст любую из обычных фун­ кций работы с :Jкраном, пре,:~,остан,1яемых операц110ююii систсмоii. Это происхuдит потuму, что ШОS хранит пuложс:ннс кур~:ора н своих переменных, для того чтобы иметь возможность управляп, страницами дисплея [ 4.5 .3 ]. После того как вы восстановите регис­ тры 14 и 15, курсор переместится в соответствующую позицию, но при следующем вызове прерывания вывода на экран курсор вер­ нется назад к той позиции, в которой он должен находитI,ся соr­ щ1сно значениям переменных BIOS. 4.1 .6 . Созданне альтернативных тнпов курсора Все прерывания операционной системы, связанные с выводом на экран, используют курсор. Вы можете изменит~, форму курсора с помощью техники, показанной в 14.2 .4 ], или сделать курсор невидимым (4.2 .3 ]. Возможны алI,тсрнат11вные типы курсора, когда вывод на экран осуществляется с помощI,ю метода прямого отображения в память (4.3 .1 ]. При этом "истинный" курсор выключается, поскольку он не будет адресовать символы в опреде­ ленную позицию видеобуфера. Вместо этого создастся "фаль­ шивый" курсор с помощью байта атрибутов. Наиболее эффективным методом является установка атрибута вывода в негативе для символа, на который указывает курсор. Для черно-белого экрана в качестве этого атрибута следует применят~, код ASCII 112. Другой способ - заставить символ, на который ука-
204 Глава 4 зывает курсор, мигать. В этом случае надо просто добавить 128 к текущему значению атрибута, чтобы символ начал мигать, и вычесть 128, чтобы прекратить мигание. Третий способ - уста­ новить для символа режим подчеркивания (код ASCII l). И нако- - нец, в программах, использующих командную строку, можно рас­ смотреть применение специального графического символа, который • следует за последним символом командной строки, такого, как стрелки, выводимые кодами ASCII 17 или 27. Отметим, что когда программа получает ввод в нескольких режимах, вы можете помочь идентифицировать текущий режим за счет особого типа курсора. Высокий уровень В данном примере курсор формируется за счет вывода символа в позиции курсора в негативе. Переменная CURSORPOSIТION хранит смещение символа, на который указывает курсор в видео­ буфере. Это четное число в интервале от О до 3998. Прибавление к этой переменной 1 дает позицию байта атрибутов для этого сим­ вола, и, поместив туда 112, мы обеспечим вывод этого символа в •'kегативе. Переменная FORMERA TTRIВUTE хранит обычные атрибуты символа, с тем чтобы можно было восстановить их после тоrо, как курсор сдвинется. 500 '"процедура анализа поступающих расшире1111ых кодо11 560 IF EXTCOD = 77 THEN GOSUB 5000'курсор впра1ю 5000 "'процедура, сдвигающая курсор вправо 1ш од11у позш1ию 5010 РОКЕ CURPOS + l,FORMERATR 'носста11аиш111аем атр~1бут 5020 CURPOS = CURPOS + 2 ·новая позш1ня 5030 FORMERAT = PEEK<CURPOS + 1> 'сохра11яем атрибут 5040 РОКЕ CURPOS + 1,112 пклю•~аем 11е1~1п111 5050 RETURN 11се сде;щ1ю Низкий уровень Здесь тот же пример реализован на ассемблере: ;---процедура перемещения курсора на oдfly позицию ш1раnо CUR_RIТ: MOV BX,CURPOS ;полу•1еш1е по.·шцш1 INC ВХ ;указын.'lем 110 атрибут с~1м11tта
вывод на терминал моv AL,FORMERAT моv ES:(BX),AL INC вх MOV CURPOS,BX MOV AL,ES: [ВХ) + 1 MOV FORMERAТ,AL моv AL,112 MOV Е~:(DX]+l,AL ;берем сохранею1ый атрибут ;восстанавливаем ero 205 ;указываем на СJ1едующ~1й сим11ОJ1 ;сохрш1яем его смещеш1е ;получаем атрибут 1ювоrо симоола ;сохраняем ero ;помещаем атрибут вывода в 11еrати1~е ;засылаем ero для СJ1едующеrо симоола 4.3 .. Вывод символов на экран Существует мноrо .способов вывода символов на экран. Иногда просто помещают один символ, белый на черном, в текущую позиμию курсора. Другие методы более сложны, но дают больше возможностей управления размещением символов, а также их атрибутами PI цветами. Некоторые процедуры выводят на экран целые строки. Но в любом случае базовой операцией, на которой основан вывод, является помещение кода ASCI 1. выводи­ мою символа в указанную позицию видеобуфера; при этом может также записываться и байт атрибутов в следующий адрес памяти. Ваши программы могут помещать эти коды непосредственно в буфер. Этот . метод н~зывается отображением в память. Отобра­ жение в память, как Qравило, при программировании для в1,1пол­ нения заданной функции требует больше усилий, чем при исполь­ зовании процедур операционной системы, но в результате полу~ чают более быстрый вывод на экран. Фирма IBM не рекомендует применять этот метод вывода на экран, поскольку будущие изме­ нения аппаратуры могут привести к тому, что программы будут работать неверно. Но- пока все новые разработки IВМ следуют одной и той же схеме адресации, на которой основано отображение в память. 4.З. t. Вывод на экран одноrо снмвопа Все процедуры для вывода символа на экран в BIOS и DOS (а также в Бейсике) помещают символ в текущую позицию курсора и автоматически передвигают курсор на одну позицию вправо. Все они п~реносят вывод на следующую строкr при достижении конца строки, если не сделано специальных указаний отбрасывать вес символы за 80-м столбцом [4,2.2 ]. Важное различие между отдсль-
206 Глава 4 ными процедурами состоит в том, что некоторые из них вместе с символом пишут также и его атрибуты, а некоторые этого не делают. 8000:0000 65 _____,. ,А' :0001 7 - Обычный атрибут : 0002 78 -•N' ;0003 7 - Обычный атрибут : 0004 32 - Пробел :0005 7 - Обычный атрибут :0006 65 - 'А' : 0007 - Атрибут подчеркивани11 : 0008 80 - 'Р' : 0009 - Атрибут подчеркивани11 : ОООА 80 --,... 'р' :ооов - Атрибут подчеркивани11 : ооос 76 - 'L' :0000 _:__.. Атрибут подчерк~вани11 • Нечетные номера: атри6уты rAN ~PPLE А DAY..• .____ Четные номера: символы Рис. 4.2 . Отображеш1е в память i\.1111 мтюхро'1ного адаптера Как в языках высокого, так и в языках низкого уровня сим­ волы могут выводиться на экран без обычных операций печати. Вместо этого используется прямое отображение в памs1п,, при котором коды символов и их атрибуты прямо засылаются в ячейки памяти видеобуфера, соответствующие определенной позиции кур­ сора на экране. Буфер начинается с адреса В000:0000 для моно­ хромного адаптера и с адреса В800:0000 д:1я цветного графичес­ кого адаптера и PCjr. EGA использует те же адреса в аналогичных. режимах экрана. Позиции с четш,1ми но11.1ерами (начиная с нуля) содержат коды АSСП символов, а поз1щ11и с нечетными номерами - байты атрибутов. На рис. 4.2 показан участок памяти видео-
Вывод на терми1tал 207 буфера. При этих операциях позиция курсора не меняется, и при желании он может быть выключен [4.2 .3 ). Вместо курсора надо хранить переменные, служащие указателями на текущую позицию. Высокий уровень Бейсик выводит как отдельные символы, так и целые строки с помощью одних и тех же операторов PRINT и WRIТE. Как прав!f.,,О, используется PRINT; WRITE - это один из вариантов со специальными форматами вывод.1. PRINT работает с данными трех видов. Он выводит содержимое как строковых, так и число­ вых переменных, например PRINT S$ или PRINT Х. Он выводит та~же символы, вставленные (в кавычках) внутрь самого опера­ тора PRINT, например PRINT ''This words are pri11tcd", и символы, соответствующие кодам ASCII, включенffЫМ в оператор PRINT в виде операторов CHR$, например PRINT CHR$<65), что обеспечивает вывод на экран символа А (код ASCII #65). В одном операторе PRINT может выводиться много данных, при этом все три формы данных могут быть перемешаны. Отдель­ ные данные огр~ничиваются запятой или точкой с запятой. Запя­ тая приводит к тому, что следующие данные будут выводиться со следующей позиции табуляции данной строки. Точка с запятой приводит к тому, что данные печатаются на экр,tнс подряд, нс разделенные пробелами <отметим, что PRINT вставляет пробел перед выводом любой числовой переменной, а WRITE не делает этого). Обычно оператор PRINT автоматически делает перевод на новую строку при завершении, т.tким образом следующий такой оператор начнет вывод с новой строки экр(1н.1. Чтобы перенос на новую страку не происходил, ;-1адо в конце оператора PRINT поставить точку с запятой, напримс·р PRINT S$;. Для установки позиции курсора перед выводом используется оператор LOCATE. Без оператора LOCATE PRINT всегда начи­ нает вывод с первой позиции строки, в которой находится курсор. Последовательные операторы PRINT заполняют экран до тех пор, пщ:а не будет записана 24-я строка, после чеrо экран сдвигается вверх, с тем чтобы следующий оператор PRINT снова выводил 24-ю строку. PRINT может осуществлять вывод в 25-й строке только при помощи LOCA ТЕ; и это также приводит к автома­ тическому сдвигу экрана вверх. Чтобы запретить сдвиг, надо окон­ чить оператор PRINT точкой с запятой. Однако этот метод нс сработает в последних позициях строк 24 и 25. Для з,tполнсния этих позиций без сдвига экрана нужно испол1,зовать отображение в память, как показано ниже.
208 Глава 4 Вы можете включать управляющие символы 17.1 .91 внут.р1, оператора PRINT для того, чтобы ,реализовать перемещения кур­ сора внутри строки. Например, если вы поместите в строку CHR$(13), то в этой точке будет сделан возврат каретки. Если вы выведете оператором PRINТ строку "О11с" + CHR$(13) + "Two" + CHR$(13) + "Тhгее", то в результате каждое слово будет выводиться с новой строки. Коды ASCII 28-31 сдвигают курсор на одну позицию соответственно вправо, влево, вверх и вниз. Опера­ тор PRINТ, не содержащий данных, приводит к выводу возврата каретки и, таким образом, следующий оператор PR INТ будет делать выво,J, в строке через одну. - Прямое отображение в память существенно увеличивает ско­ рость вывода на экран в Бейсике. Оно особенно полезно при конст­ руировании табличного вывода, когда ,формы могут достигать пра­ вого нижнего угла экрана. Сначала надо установить указатель сег­ мента на &НВООО, а затем использовать оператор РОКЕ для засылки байтов памяти. Прилегающие по горизонтали символы отстоят друг от друга на два байта, разделяемые байтом атрибутов. Для 80-символьных экранов прилегающие по вертикали символы отстоят на 160 байт друг от друга (2 байта для каждого символа и атрибутов). В следующих двух примерах вдоль границы экрана рисуется рамка с помощью символов псевдографики. В первом примере чаще применяется оператор PR INТ, а во втором испол1,­ зуется исключительно прямое отображение в память. Отмстим, что и в первом случае приходится использовап, прямое отображение в память в последних столбцах строк 24 и 25, чтобы и3бежать сдвига экрана. • Использование PRТNT: 10 CLS: КЕУ OFF 'о•шстка экрана 20 DEF SEG = &НВООО 'указываем 11а шщеобуфер 30 LOCATE 1,1: PRINT CHR$(201) 'левый верхний у1-ол 40 LOCATE 1,80: PRINT CIIR$(187) праllый uерх1н1й угол 50 LOCATE 1,24: PRINT CHR$O86) 60 LOCATE 1,25: PRINT CHR$(200) 70 РОКЕ 3838,186 ·познцf1Я 80 строкf1 24 80 РОКЕ 3998)88 ·штщня 80 строкfl 25 90FORN=2то79 Г{}\НIЗOIIПLJll,IIЫC JlflllШI 100 LOCATE l,N: PRINT CI-IR$(205);: l,OCATI:: 25,N: l'RINT CIIR$(205) 110 1\/ЕХТ 120FORN=2ТО23 uертнкат,ные ш1ш1f1 130 1,ОСАТЕ N,I: PRINT CHR$O86): LОСЛТЕ N,80: PRINT CHR$(186) 140 "IEXT
Вывод на терминал йспользование прямого отображения II nамип,: 1О CLS: КЕУ OFF 20 DEF SEG = &НВООО 30 РОКЕ 1чо1 40 РОКЕ 158,187 50 РОКЕ' 3840,200 60 РОКЕ 3998,188 70FORN=2ТО156STEP2 80 РОКЕ N,205: РОКЕ N + 3840,205 90 NEXT 100FORN=160ТО3680STEP160. 110 РОКЕ N,186: РОКЕ N + 158,186 120 NEXT Средний уровень .. ·011~~стка экрана 'буфер MOIIOX(IOMl\011) .ЩICIIJlбl 'левый 11ерх11ий у1'0л 'правый 11ерхш1й угол ·левый нижний угол правый НИЖШIЙ угол гор~1зонталь111,1е прямые ·как верхняя, так и нижняя вертикалы1ые прямые , _ нравая и левая 209 Операционная система предоставляет шесть процедур вывода на экран - три в BIOS и три •в DOS. Они различаются главным образом тем, передвигается ли курсор после вывода символа, вызывают ли они сдвиг ,экрана, позволяют ли 9ни устанавливат1, атрибуты и цвета символов, а также какие управляющие коды они интерпретируют •(некоторые рассматривают символ <<Backspace>> rtpocтo как обычный символ, а некоторые действительно сдвигают курсор на одну позицию назад). Эти шесть процедур следующие: Прерывание !ОН: функция 9 А выоод с~1м11ола с атр~1бутами выоод символа без атрибуто11 Е "телетМ1шшя" процедура (как на nршпер) Прерывание 21 Н: функция 2 вывод символа без атрибутов 6 вывод символа без атрибутов 9 вывод строки CflMIIOJIOB Функции 9 и А прерывания l ОН вообще не интерпретируют управляющие символы. Функции DOS интерпретируют управ-
210 Глава 4 ляющие коды, приведенные в следующей таблице. Функция Е прерывания I0H интерпретирует все коды таблицы, кроме ASCII 9.• ASCII 7 звонок ЛSCII 8 возврат на шаг (Вackspacc) ASCII 9 табул,щия ASCII 10 перевод строки ASCII 13 возврат каретки Первые две функции прерывания l0H не передвигают курсор после вывода символа. Функция 9 этого прерывания выводит на э-кран с указанием атрибутов, а функция А - без указания, при этом сохраняется текущее значение байта атрибутов для этого символа. AL должен содержать выводимый символ, а BL - атри­ буты. Номер страницы дисплея содержится в ВН. Он должен указываться даже для монохромного дисплея, который имеет только одну страницу памяти дисплея. В этом случае должна быть установлена первая страница, которой соответствует номер О. Особое свойство этих двух функций BIOS состоит в том, .что символ выводится такое число раз, какое указано в СХ. Обычно указывают СХ равным 1, но эти функции могут JJerкo выводить целые строки символов, если указать большее значение счетчика, - полезное свойство при создании рамок. Отметим, что даже если выводится много символов, позиция курсора не изменяется. Когда строка выводимых символов займет все свободное пространство экрана справа-вниз от курсора, вывод будет перенесен в первые позиции экрана. • ;---вывод символа в негативе MOV АН,9 ;функция записи с атрибутами моv AL,THE_ CHAR ;си>.~вол в ЛL моv BL,112 ;атрибуты в BL моv ВН,О ;страница 1 моv СХ,1 ;вывести од~ш раз INT lOH Вместо того чтобы постоянно восстана:ыивать значение счет­ чика в СХ, прерывание BIOS предоставляет телетайпную проце­ дуру, которая больше подходит для вывода строки символов. Эта процедура выполняется с помощью функции Е. Подготовка к ней
Вывод па тер.мииал 211 такая же, как и для функции А, но не надо засылать значение в СХ. Строка выводится за счет изменения символа в AL и повтор­ ного вызова прерывания. При использовании в графическом режиме в BL устанавливается цвет палитры, в противном случае сохраняется старый атрибут. ;---вьшод строки с помощью телетайшюй процедуры MOV AH,0EII ;1юмср функции моv вн,о LEA BX,STRING NEXT: MOV AL, (ВХ] СМР ЛL,'$' JE ALL_DONE INT !ОН INC ВХ ;1юмер страницы ;ВХ указ,,шает на строку • ;берем с~1м1юл 11 ЛL ;про11срка на конец строки ;если да, то пыход ;вывод строки ;переход~,м к следующему п1мполу JMP SHORT NEXT • ;повторяем процедуру ЛLL DONE: Прерывание DOS 21 Н, как правило, предоставляет более полезные процедуры, перемещающие курсор и приводящие к сдвигу экрана при достижении нижней строки, а также интерпре­ тирующие некоторые. из обычных управляющих кодов. Функции DOS осуществляют вывод на страницу, которая должна быть уста­ новлена функцией 5 прерывания l ОН [4.5.3 ]. Предоставляются две функции для вывода символа - с номерами 2 и 6. Первая из них распознает <<Ctrl-Break>> [3.2.8 ], а вторая нет. (Когда с клавиатуры вводится <<Ctrl-Break», процедура обработки <<Ctrl-Brcak» нс выполняется до тех пор, пока нс используется функция, которая распознает его наличие.) Обе функции выводят белые символы . на черном фоне до тех . пор, пока не сделана специальная установка цвета с помощью драйвера устройства ANSI.SYS [4.1 .3 ]. В общем, необходимо -только поместить символ в DL, номер функции - в АН и вызвать прерывание 21Н. Однако-функция 6 имеет и второе назначение - ввод с клавиатуры. Она выступает в этой роли, только если в DL помещен код FF [3.1 .5 ]. Во всех остальных случаях она выводит на экран содержимое DL. В следующем примере функция 6 пооче­ редно принимает и печатает символ (в [З.1.4] обсуждается проце­ дура, которая комбинирует оба этих свойства). MOV ЛН,6 NEXT: MOV DL,0FFH ;номер функции ;при этом з1ш•1ешш прнш1м;~ем шю11
212 Глава 4 INT 21Н ;выполняем прерывание JZ NEXT ;если не было ввода, то обратно СМР AL,13 ;это был возврат каретки'' JE END INPUT ;если да, то на конец MOV DL,AL ;шшче,посыласм символ в DL INT 21Н ;~• выводим его на экран JMP SHORT NEXT ;повторяем процедуру Низкий уровень На нижнем у·ровне весь вывод на экран осуществляется через отображение в память. Такой метод не рекомендуют, чтобы нс столкнуться с проблемой совместимости с будущими поколениями машин. Однако видеобуфер микрокомпьютеров фирмы IВМ устроен одинаково и расположен в одних и тех же адресах памяти. Поскольку буфер устроен таким образом, •что байты атрибутов перемежаются с байтам·и символов, символьные данные не могут просто пересылаться из памяти в буфер инструкцией MOVSB, поскольку указатель в буфере должен увеличиваться на два после каждого переноса байта. Однако такой способ существенно уско­ ряет вывод на экран. Отметим, что отображение в памяп, нс рабо­ тает при выводе символов в графическом режиме. В этом случае размер видеобуфера lбК или 32К и ВIOS рисует каждый символ поточечно. Отметим также, что при' отображении в память не используется курсор для указаt1ия на символ. При желании можно перемещать курсор по мере ввода (4.2.l] или выключить его и создать свой псевдокурсор [4.2.6 ]. ;---в сегменте данных SAM STR DB 'PRINT TНIS STRING$ ;---вывод строки 'моv АХ,080001-1 ;мо1юхром111,1й ЩICIIJICЙ MOV ES,AX ;указьшасм 11а 1шдеобуфср LEA BX,SAM_STR ;ВХ указьшает 11а строку MOV DI,CUR_ST ;начальная позиция о буфе·ре NEXT: MOV AL, [ВХ] ;берем символ СМР AL,' $' ;проверка на конец строки JE ALL DONE ;если да, то выход MOV ES: [DI] ,ЛL ;ш~аче - помещаем сим1юл 11 буфер INC DI ;у11ели'lш~асм указатст, 11а 2 INC DI
.Вывод на терминал 213 INC DX ;переходим к обработке ·следующего символа JMP SHORT NEXT ALL_DONE: Для цветноrо графическоrо адаптера и PCjr <но не для EGA> существует проблема, связанная с отображением в память. Когда запись в буферную память происходит одновременно с чтением ее для вывода на экран, на экране возникает интерференция. Эта проблема решается ожиданием сигнала "все чисто" (all clear) перед записью в видеобуфер. Надо непреJ?ывно читать значение из порта ЗDАН. Когда бит О равен l, можно спокойно писать. (ЗDАН - это порт, через который PCjr посылает данные массиву ворот дисп.лея; когда из неrо читаем, он возвращает регистр статуса, как и для цветноrо адаптера.) ;---ожидаем пока все чисто MOV DХ,ЗDАЦ СИ_AGN: IN AL,DX TESTAL,l JNE CH_AGN ;---теперь выводим сообщение LEA BX,MESS MOV Dl,2000 NEXT: МОУ ЛН,010000018 МОУ AL,(BXJ СМР AL,'$' JE ALL_DONE МОУ ES: (DI) ,АХ INC ВХ INC DI INC DI ;порт регистра статуса ;получаем значеш1с ;проuерк11 11ер1юго бита ;есш1 он О, то обр:тю ;1111•ш1шем 111,шод с 11е1пра :~кpaiia ;11трибу1· Сf1ш1й 1111 кр11с1юм ;берем CИMIIOJI ;проверяем 1111 конец строкft . ;если коне,~. то на 11ых<щ ;ин11 11е - выводим CflMIIOЛ ;унеш111иваем указатет, стр<жf1 ;увеш1чив.'\ем указател1, буфера JMP SHORT NEXT ;обр11бать111аем сдедующиii сим1юJ1 ALL_DONE: Попробуйте поэкспериментировать, сколько символов за один цикл может выводить ваша процедура без появления интерферен:. ции. Имейте в виду, что при первом выполнении цикла тести­ руемый бит может быть равным единице. Однако может не остать­ ся времени, чтобы завершить операцию записи. PCjr специально сконструирован таким образом, что вывод в адреса, используемые буфером цветного графического дисплея,-
214 Глава 4 перенаправляется в ту область памяти, где находится буфер. Это свойство позволяет создавать программное обеспечение, подхо­ дящее для обеих систем. 4.3 .1 . Вывод строкн символов на экран Процедуры, которые выводят целые строки символов, очень полезны, но они могут накладывать ограничения на содержимое выводимой строки. Надо обращать внимание на то, какие управ­ ляющие коды (табуляция, пробел и т.п.) интерпретируются, а какие нет. До появления АТ BIOS не имел функции вывода строки, хотя в MS-DOS всегда существовала такая функция. Функция ВIOS предоставляет больший контроль над атрибутами символов. Естественно, что ее применение создает проблему сов­ местимости с предыдущими машинами. Напоминаем, что EGA имеет ПЗУ, расширяющее ROM-BIOS, и функция вывода строки символов является одним из таких расширений. В этом случае любой IВМ РС и ХТ имеет возможность использовап, эту процедуру. ~,.1сокий уровень Бейсик выводит строку точно так же, как и отдельные символы. Надо просто написать PRINT S$, где S$ может быт1, любой строкой длиной до 255 символов, которую сконструировала программа. Интерпретируются 10 управляющих кодов. а именно: ASCII 7 звонок ASCII 9 табуляция ASCII 10 11ере11од строк~• ASCII 11 курсор 11 11ерI1ую I10з~щню :жраI,а (1 lошс) ASCII 12 11срс11од формата (стирает экра,I + lloшc) ЛSCII 13 11оз11рат каретки ASCII 28 курсор 111Iра,ю ASCIJ 29 курсор IIJICIIO ASCII 30 курсор I111срх ASCII 31 курсор IIШIЗ Все остальные коды выводятся на экран как символы.
Вывод на терминал. 215 Средний уровень Функция 9 прерывания· 21Н выводит строку. DS:DX должны указывать на первый символ строки. Строка должна завершаться символом $, что означает, что сам символ $ не может входить в строку. Строка может быть любой длины. Функция не переводит автоматически курсор на начало следующей строки после завер~ шения вывода; чтобы это выполнялось, надо добавить в конец строки символы ОАН (перевод строки) и 0DH (возврат каретки). ;---в сегменте данных FIR_SТR DB 'Thls 1s the first string',0AН,0DH,'$' SEC_STRDB' 'Лпd this is the.second string$' ;---11ывод строки MOV АН,9 ;номер функции 11ывода строк11 LEA DX,FIR_STR ;загружаем адрес пераой строки INT 2\Н ;печатаем строку с поз11ции курсора LEA DX,SEC_SТR ;загружаем адрес второй строки INT 21Н ;печатаем строку с начала ноаой строки Интерпретируются следующие управляющие коды: лsсп 7 звонок ASCII 8 возврат на шаг (Backspace) ASCII 9 табуляция ASCII 10 перевод строки ASCII 13 uозnрат каретки Функция DOS 40Н прерывания 21Н также полезна при выводе строк на экран. Она требует, чтобы вы знали длину строки, поскольку она не нуждается в символе-ограничителе; эта функция особенно удобна для дампа текстовых файлов на экран. Исходно эта функция была предназначена для вывода в файл. Она требует дескриптора, который является идентификационным номером для данного файла или устройства. Щiсплей имеет заранее предназ­ наченный дескриптор #1, Надо поместить дескриптор в ВХ, а число байтов строки - в СХ. DS:DX должны указывать на строку. Функция выводит текст с нормальными (белый на черном) атрибутами. Отметим, что не надо предварительно "открывать" дисплей, как это вы деАаете с другими файлами при использовании этой функции. Вот пример:
216 Глава 4 ;---sыuuд 1000 байт 1екс-н1 MOV АН,40Н ;1юмер фу11кцш1 MOV ВХ, 1 ;дескрfштор днс11ле11 LEA DX.STRING ;з..1rружаем адрес строк11 MOV СХ,1000 ;•шсло sы1юднмых байтов INT 21Н MS-DOS предоставляет набор Еsс-последовательностей, кото­ рые являются специальными управляющими строками для аппара­ туры. Когда они выводятся с помощью функции 9 прерывания 21Н,. они могут управлять курсором, режимом дисп,,ся, цветом СИМВ()ЛОВ и некоторыми аспектами клавиатуры. В приложении Д обсуждается, как их использовать. Когда программа выводит на экран мноrо строк, Еsс-последовательности часто являются самым удобным способом позиционирования_ курсора и установки цвета строки. Это происходит потому, что они сами рассматриваются как очередные строки в серии выводимых строк. У АТ и машин, снабженных EGA, функция 13Н прерывания l0H выводит строку. ES:BP должны указывать на строку, а длина строки должна быть в СХ. DX указывает позицию курсора, откуда будет начинаться строка (вычисляемую как смещение от начала страницы, на которую идет вывод без учета байтов атрибутов). В ВХ должен быть указан номер страницы. Наконец, номер кода от О до 3. содержащийся ·в AL, указывает способ вывода строки. • AL 0 строка COl"IOIIТ TOJlt,K(J fl3 Cll~Ш()J\()11, курсор IICll(ЩIШЖCII ЛI, 1 строка COCTOll'I TUJll,KO нз CIIMIIOJIOII, курсор 1\IIIIЖCTCI/ AL 2 в строке •1ередуются CHMIIOJIЫ и атрибуты, курсор IICl\(ЩIIHЖCII AL 3 в строке •1ере11уются снм1юлы и атр11буты, курсор дп11жется " Когда AL равен О или 1, атрибуты должны находиться в BL. Все символы будут выводиться с· этими атрибутам11. Данная функция интерпретирует возврат на шаг, перевод строки, возврат каретки и звонок как управляющие команды, а не как печатаемые символы. Низкий уро!Jень Ограничение н·а 11спользование символа $ делает функцию 9 бесполезной' для большинства приложений. Однако на многих машинах это единственное прерывание, доступное для вывода строки неизвестной длины. Попробуйте написать свое собственное прерывание (в [1.2 .3 ] показано как), использующее технику отоб­ ражения в память [4.3 .1 ]. Возьмите в качестве ограничителя
вывод на термииал 217 какой-нибудь специальный символ, например ASCII О, вместо $. Пусть эта процедура обрабатывает только те управляющие коды, которые нужны вам. Такой метод будет работать намного быстрее, чем при использовании функции MS-DOS. 4.3.3 . Чтение символа и ero атрибутов в данной пози·ции Обычно программа получает данные из своих переменных и помещает их в видеобуфер для вывода на :жран. В некотором смысле программа "знает", что на ::>кране. Но встречаются ситу­ ;щии, в которых сам видеобуфер используется как рабочая область (например, в графических программах вырезки и вставки) и теку­ щее содержимое экрана не записано в памяти программы. В этих случаях иногда· необходимо прочитать с экрана, вместо того чтобы вывести на него. Функция BIOS позволяет прочитать символ и его атрибуты в определенной позиции экрана; другой способ состоит в обращении метода прямого отображения в память дисплея \4.3 .1 ]. Чтобы прочитать символ и атрибуты в строке О и столбце 39 ( 1,40 в Бейсике) в режиме 80 символов в строке, надо сложить (0 х 160) плюс (39 х 2) и взять результат в качестве смещения в видео­ буфере. Смещения для различных страниц обсуждаются в [4.5 .3 \. Имейте в виду, что обращение метода прямого отображения в память не будет работать при выводе символов в графическом режиме. Высокий уровень В Бейсике функция SCREEN • служит для получения символа или атрибутов (эта функция не имеет ничего общего с оператором SCREEN, устанавливающим режим дисплея). SCREEN 5,10 полу­ чает код ASCII символа, расположенного в строке 5, столбце IО (строки и столбцы нумеруются от 1). Чтобы получить атрибуты символа, надо добавить третий параметр 1, напри.мер SCREEN 5, 1О, 1. В графическом режиме данная функция возвра­ щает О, если требуемая позиция экрана не содержит (нсмодифи- цированного) символа. .• .. . Атрибуты также возвращаются в виде кС1да от О до . 255. Поскольку Бейсик не позволяет испол1,зования двоичных чисел, требуются некоторые манипуляции, чтобы определить. атрибуты. Основной цвет равен ATT,RIВUTE MOD 16. После того как вы выделили основной цвет, цвет фона определяется по формуле {((ATTRIВUTE - FOREGROUND) / 16) MOD 128) .. Если байт атрибутов больше 127, то включено мигание (или, при соответ-
218 ствующей установке, включены интенсивные цвета фона 14.1 .3 1>. В приложении Б обсуждаются битовые оnсращ111 в Бейсике. Средний уровень Функция 8 прерывания lOH возвращает символ и его атрибуты для текущей позиции курсора. В ВН должен содержаться номер текущей страницы дисплея (отсчитываемый от О и всегда равный О для монохромного дисплея). Код символа возвращается в AL, а байт атрибутов в АН. Эта функция настолько мощная, что способна даже читать символьr в графическом режиме, сообщая цвет палитры в АН. Она работает даже для символов, опреде­ ляемых пользователем [4.3.4 ], В примере определяются символ и атрибуты в позиции 0,39 для страницы 2 графического адаптера: ;---устшю11ка поз~щии курсора моv АН,2 ;фу11кция успшо11ки курсора MOV DH,0 ;,юмср строк~, MOV DL,39 ;11омср .стш16ц11 MOV вн,о ;номер стр11шщ1,1 INT l0H ;позиционируем курсор ;---чтение символа и атрибутов MOV АН,8 ;функф1я чте11ия сим110Jш/атрибупт MOV ВН,2 ;номер стра11~щы INT I0H ;11 A/-1:AL тсnер1, атрибуты ~• сим1юJI Низкhй уровень Надо вычислить смещение и проделать операцию, обратную прямой записи в память. При необходимости надо добавить смеще­ ние для данной страницы. В примере получаем символ и атрибуты в позиции 7,39 страницы 2 графического адаптера: ;---чтение символа и атрибутов позиции 7 ,39 страницы 2 MOV AX,0BSOOH ;адрес видеобуфера м(>v ES,AX MOV Dl,IOOOH MOV AL,80 MOV BL,7 MUL BL MOV АХ,39 ;ES указ1,1щ1ст 1111 nер1171й баrп б:фера ;смещевне до 1ш•111ла страшщы ;ум,южаем ,юмср строки 11а 160 ;номер строк~, ;теперь II АХ (строка-\ )х i 60 ;,юмер столбца
Вывод 11а терми11ал ADD ВХ,ЛХ SJ-IL ВХ,1 MOV AX,ES: [ВХ] [DI] ;номер 1юз~щн~1 11 шщсобуфсрс ;умtюжасм ст 11а щш ;тенерь АН:ЛI. содержат атрнбуты/снм1юл 4.3.4 . Созданне спецнаnьных снмвоnов 219 Только монохромный адаптер не может выводить символы вида, заданного самим программистом. Цветной· адаптер поддер­ живает 128 символов, определяемых пользователем, PCjr - 256, а EGA - 1024, из которых одновременно доступно 512. Для цветного адаптера ROM-ВIOS содержит данные для разрисовки только пер­ вых 128 символов набора ASCII (с номерами от О до 127). Следу­ ющие 128 символов недостvпны для вас, пока вы не создадите их, используя описанную здесь технику. Отметим, что в MS-DOS 3.00 имеется команда GRAFTABL, которая предоставляет требуемые данные для второй порции из 128 символов. PCjr имеет готовые данные для второй порции из 128 символов; а EGA - полные наборы символов для режимов с 200 и с 350 строками. Символы для графического адаптера и PCjr описываются с помощью матрицы 8*8 точек. Данные для каждого символа содер­ жатся в восьми байтах. Каждый байт содержит установку для точек одного ряда, начиная с верхнего, причем старший бит (номер 7) соответствует самой левой точке в ряду. Когда соответ­ ствующий бит равен 1, то·чка высвечивается. Для описания сим­ вола вы должны определить правильные последовательности битов для восьми байтов и поместить их в· последовательные ячейки памяти. На рис. 4.3 показано, как 8 .байт опи~ывают бубновую масть. Все 128 символов вместе требуют 1024 байта, хотя вовсе не обязательно, чтобы были описаны все символы. Специал1,ный вектор прерывания (постоянный указатель в младших адресах памяти [1.2.0 ]) указывает адрес первого байта первого символа расширенного набора, т.е. символ номер 128. Когда в позицию символа в видеобуфере посылается код 128, просматриваются и выводятся первые восемь байт. Если номер символа 129, то выво­ дятся байты с девятого по шестнадцатый и т.д. Номер этого вектора прерывания 1FH и он расположен по адресу ОООО:007С. Поместите значение смещения в младшее слово (сначала младший байт), а адрес сегмента - в старшее слово (снова сначала младший байт). Отметим, что можно определять символы с большими номерами кодов, не отводя памяти для символов с меньшими номерами; надо просто, чтобы вектор указы­ вал на некоторый адрес, который меньше, чем адрес начала блока, содержащего данные для описания символов. Восьмибайтовые
220 Глава 4 последо~ательности, описывающие символы ASCII с кодами 128- 255, приведены в [4.3 .5). У PCjr вектор lFH указывает на вторые 128 символов ASCII, а вектор 44Н - на первые. Оба этих вектора можно изменить, допуская полный набор 256 симВОJ1ов, опреде-. ляемых пользователем. 3н8'18НН11 12864321684 21 б11ТО8 оо ~ о о ~ о о J-1 о о~1: о оJ- u • ! о о J- о о J- оо ~ Рис. 4.3. Цепочка битов, образующа11 буб11ов)UО масть Для EGA картина намноrо сложнее, хотя и более гибкая. При ин1щиализации текстовою режима один из двух наборов символов (8•8 или 8•14) копируется из ПЗУ EGA в карту битов 2 видео­ буфера. Эта часть буфера рассматривается как разбитая на блоки, причем стандартный набор символов помещается в блок О. При условии, что EGA оснащен достаточной памятью, могут быть определены еще три блока ддя описания символов. Размер блока определяется числом строк матрицы, используемой ддя опи~1ния символа. Символы, описываемые матрицей 8•8, требуют 8 х 256, • или 2048 байт. Когда разрешено более одноrо блока символов, бит '
Вывод на терминал 221 З байта атрибутов определяет, из какого~ блока будут браться· данные для описания символа. Какой из блоков будет необходим - зависит от установки битов О-3 регистра выбора карты символов, адрес порта КОТОJЮГО 3С5Н. Предварительно надо послать 3 в порт 3С4Н, чтобы указать требу­ емый регистр. Биты 1-0 дают номер блока символов, который берется, когда бит 3 байта атрибутов равен О, а биты 3-2 - делают то же самое, когда бит 3 равен 1. Когда уст~новка обе.их пар битов совпадает, возможность использования двух набОJЮВ символов отсутствует и бит 3 байта атрибутов переключается на установку интенсивности символа. В этом случае используется только блок О. Однако ничто не может помешать вам поместить свои символы в любую нужную вам позицию в этом блоке. Если вы изменили стандартный набор символов, то можно в любой· момент восста­ новить его из ПЗУ. Высокий уровень В Бейсике вы должны позаботиться о том, чтобы данные, описывающие символы, находились за пределами памяти, исполь­ зуемой ПJЮГраммой. Если памяти много, то можно поместить данные в старшие адреса; если имеется опасность конфликта, то следует примен:ить команду CLEAR для ограничения .количества памяти, которую может использовать Бейсик. Затем нужно помес­ тить адрес первого байта данных в вектор прерывания. В следу­ ющем примере описывается символ 128 как квадратная рамка. Операторы DAТА содержат значения, описывающие символ. Они равны либо 255, либо 129; в первом случае все биты равны 1, а во, втором равны 1 только крайние . биты. Вычисление десятичных • значений, соответствующих данным цепочкам битов, рассмотрено в приложении Б. 100 '"помещаем данные, начиная с адреса &НЗООО _ 110 DATA 255, 129, 129, 129, 129, 129, 129, 255'да1И1ые для одного символа 120 DEF SEG = ·&нзооо ·указываем начало сегмента 130FORN=ОТО7, •определяем 8 байт 140 READ Q ·читаем I байт 150 РОКЕ N,Q ·помещаем его в память 160 NEXT 'и т.д. 170 --·установка вектора прерывания 180DEFSEG=О 190 РОКЕ 124,0 200 РОКЕ 125,0 210 РОКЕ 126,0 220 РОКЕ 127,&НЗО ·указываем на начало памяти ·указываем смещение ·указываем сегмент
222 Глава 4 230 '"'печатаем символ 240 LOCATE 12,12: PRINT CHR$(128J ·теперь есть символ 128 Средний уровень Для цветного адаптера и PCjr для изменения вектора преры­ вания lFH применяйте функцию 25Н прерывания 21Н. При входе DS:DX должны указывать на первый байт блока данных. Более подробное описание см. в [l.2.3 ]. В примере создаются два символа с номерами 128 и 129. Они являются зеркальными отобра­ жениями друг друга, а выведенные подряд образуют. небольшой прямоугольник. ;---в сегменте данных CHAR_DAT D8 111111118, 100000008, 100000008, 100000008 D8 100000008, 100000008, iooooooo8, 111111118 DB 111111118, 00000001 В, 000000018, 000000018 D8 000000018. 000000018, 000000018, 1111 111 18 ;---установка вектора прерывании PUSH DS LEA DX,CHAR_DAT MOV AX,SEG CHAR_DAT :Моv DS,AX MOV АН,25Н MOV AL,lFH INT 21Н РОР DS ;---печать символов MOV АН,2 MOV DL,128. INT 211-1 MOV DL,129 INT 2\Н ;сохра1Iяем DS ;смещение для да~н11,Iх в DX ;сеrмеIIт ДJIя дашн,Iх в DS ;функция уста~ювки I1ектора ;номер изменяемого вектора ;установка вектора ;восстана~~лш,аем DS ;номер функции ;11Cplll,lfl CИMIIOJI ;вывод е11> ;11торой CHMIIOJI ;вывод е1'0 Для адаптера EGA характерна функция 11 Н прерывания 1ОН, которая манипулирует набором символов. Эта функция может быть очень сложной, когда она применяется для создания спе­ циальных режимов экрана, но ее основное назначение достаточно простое. Существуют четыре подфункции. Когда AL равен О, данные, определяемые пользователем, переносятся из памяти в специальный блок символов. Когда AL равен t или 2, наборы данных для символов 8*14 и 8*8 соответственно копируются из ПЗУ в блок символов. Когда AL равен 3, функция устанавливает назначение блока в регистре .!iЫбора карты символов, как описано выше. В последнем случае надо просто поместит,, соответствующие
вывод на термин.ал 223 данные в BL и вызвать функцию. Для загрузки данных из ПЗУ поместите номер блока в BL и выполните функцию. Для загрузки своих данных надо, чтобы ES:BP указывали на них, число переда­ ваемых символов должно быть в СХ, смещение (номер символа) в блоке должно быть в DX, число байтов на символ - в ВН, а номер блока - в BL. После этого вызывайте прерывание lOH. Вот пример: ;---устанаВJJиваем 128 пользовательских символов в блоке О MOV AX,SEG CHAR_DAT ;ЕS:ПР должны указыn.1ть на д111шые MOV ES,AX MOV BP,OFFSET CHAR_l)AT моv СХ,128 ;ЧflСЛО символов MOV DX, 128 ;1ш•111лыюе смещение MOV BL,0 ;номер блока MOV ВН,8 ;матрица 8*8 MOV AL,I MOV AH,llН INT lOH ;номер nодфункции ;номер функции ;переносим данные Вы можете получить информацию о статусе имеющегося набора символов с помощью nодфункци1:1 ЗОН функции 11 Н прерывания lOH. У этой подфункции нет входных регистров, а при возврате в СХ содержится число байтов, отведенных Аля каждого символа, а в DX - сколько рядов точек символ занимает на экране (оба этих параметра зависят как от размера символа, так и от вертикального разрешения экрана). 4.3 .S. Сводка данных для оnнсання символов Ниже приведены 8-байтовые последовательности, необходимые для описания символов для цветноrо графического адаптера. Их использование объяснено в [4.З.4 ]. Ко8 ASCJI ~ ПocлelJOIIHTCЛЫIOCТI, ~111eCTIIH1!1!1\Пl~fl'III/III) 128 с 78сесосе7818ос78 129 (j 00се00сесесе7Е00 130 е 1С0078сеFCccl7800 131 ii 7ЕС33С063Е663F00 132 а се0078Ос7Ссе7Е00- 133 а ЕО0078ос7Ссе7Е00 134 а 303078ос7Ссе7Е00 135 с 000078ос7Ссе7Е00
224 Глава 4 К!ШАSСП Символ Послевовательность (111есп1ав11ап11!ичнаяl 136 е 7ЕС33С667Е603С00 137 ё се0078се-FCсо7800 138 ё ЕО0078сеFCсо7800 139 1 се00703030307800 140 7СС6381818183С00 141 ЕО00703030307800 142 А С6386СС6.FEС6С600 143 А 30300078сеFCсе00 ,, 144 t 1С00FC607860FC00 145 • 00007Fос7Fсе7F00 146 1 3Е6СсеFEсесеСЕ00 147 о 78i;c0078сесе7800 148 о 00се0078сесе7800 149 о 00ЕО0078сесе7800 150 u 78се00сесесе7Е00 1~1 u 00ЕО00сесесе7Е00 152 у 00се00сесе7СосF8 153 !) С3183С66663С1800 154 u се00сесесесе7800 155 с 18187Есосо7Е181·8 156 ( 386С64FO60Е6~с00 157 " сесе78FC30FC3030 ч ,i 158 ll F8сесеFAС6CFС6С7 ·! 159 f ОЕ1В18-3С1818D870 160 а 1С0078007Ссе7Е00 161 3800703030307800 162 6 001С0078сесе7800 163 u 001С00се.сесе,7Е00 164 i'i 00F800F8сесесе00 165 А FC00сеЕСFCDCсе00 166 3С6С6С3Е007Е0000 167 ! 386С6С38007С00ао i1J ··]
Вывод на терминал 225 Ко8 ASCII ~ Послеl!ОRателыюсп, (111есп1а111щп1[!~••11~а11) 168 (, 30003060сосе7800 ]69 000000FCсосо0000 170 000000FCосос0000 171 1⁄2 С3С6сеDE3366сеOF 172 v. С3С6сеDB376FCF03 173 1818001818181800 174 (( 003366се66330000 175 » 00се663366се0000 176 2288228822882288• 177 55АА55АА55АА55АА 178 ~ DB77DBЕЕDB77DBЕЕ 179 1 1818181818181818 180 -1 18181818F8181818 181 ~ 1818F818F8181818 182 ~ 36363636F6363636 183 1 00000000FE363636 184 , 0000F8.18F8181818 185 i 3636F606F6363636 186 1 3636363636363636 187 ,. 0000FE06F6363636 188 .11 3636F606FE000000 189 J 36363636FE000000 190 J 1818F818F8000000 191 1 00000000F7181818 192 l 181818181F000000 193 l. 18181818FF000000 194 т 00000000FF181818 195 ~ 181818181F181818 196 00000000FF000000 197 + 18181818FF181818 198 ~ 18181F181F181818 199 • 3636363637363636 В Р. джордейн
226 Глава 4 Ко[.\ A§CII CИMIIOJI ПocJJCJIOIЦIтел1,1 l<Х.-ТЬ ( 111еспIRJ!I !Rти 1!~• •ша И) 200 L 363637303F000000 201 r 00003F3037363636 202 .L 3636F700FF000000 203 т 0000FF00F7363636 204 •• 3636373037363636 205 0000FF00FF000000 206 + 3636F700F7363636 207 J. 1818FF00FF000000 208 J. 36363636FF000000 209 т 0000FF00FF181818 210 т 00000000FF363636 211 L 363636363F000000 212 L 18181F181F000000 213 r 00001F181F181818 214 r 000000003F363636 215 t 36363636FF363636 216 + 1818FF18FF181818 217 J 18181818F8,000000 218 г 000000001F181818 219 1 FFFFFFFFFFFFFFFF 220 • 00000000FfFFFFFF 221 1 FOFOFOFOFOFOFOFO 222 1 OFOFOFOFOFOFOFOF 223 • FFFFFFFF00000000 224 а 000076оссвDC7600 225 /J 0078сеF8сеF8сосо 226 r 00сесосососо0000 227 1r 00FE6С6С6С6С6С00 228 ~ FCсе603060сеFC00 229 (1 00007Е0808D87000 230 ,,, 00666666°667С60со 231 .,. 0076ОС1818181800
Вывод на fпермипал 227 , Ко8 ASCII Символ ПослеlJовательность (111естнаl!•!ати~ичная) 232 t FC-3078сесе7830FC 233 е 386СС6FEС66С3800 234 n 386СС6С66С6СЕЕ00 235 о 1С30187Ссесе7800 236 11D 00007ЕDBDB7Е0000 237 ф 06ос7ЕDBDB7Е60со 238 li 3860соF8со603800 239 n 78сесесесесесе00 240 = 00FC00FC00FC0000 241 t 3030FC303000FC00 242 ~ 6030·18306000FC00 243 / ~ 183060301800FC00 244 r ОЕ1В1В1818181818 245 J 1818181818,D8D870 246 .,. 303000FC00303000 247 "' 0076DC0076DC0000 248 386С6С3800000000 249 0000001818000000 250 0000000018000000 251 ✓ OFосfJtосЕС6С3С1С 252 Т/ 78.6С6С6С.6С000000 253 7018306078000000 254 . 00003С3С3С3С00OQ \ 255 (Ьlank FF) 0000000000000000 1. 8*
228 Глава 4 4.4. Вывод точечной rрафнк-н \ Цветной графический адаптер имеет три графических режима, PCjr - шесть, а EGA - семь. Как устанав­ ливать эти режимы, . показано в [4.1 .2 ]. Требования к размеру памяти существенно различаются для доступных режимов в зави­ симости от разрешения экрана и числа цветов. В своих улуч­ шенных графически:и режимах EGA использует память _дисплея совсем по-другому, чем остальные видеосистемы, но он rочно 3му­ лирует их методы работы с памятью в трех общих режимах. Сначала рассмотрим цветной адаптер и систему PCjr. Два цвета (черный и белый) требуют только один бит памяти для каждой точки на экране. Четыре цвета занимают 2 бита, а 16 цветов - 4 (8-цветные режимы не используются, поскольку три бита, требующиеся для их представления, недьзя удобно размсс-, тить в 8 битах байта). Для всех режимов по вертик,1ли имеется 200 точек. Низкое разрешение (используемое только на PCjr) выводит 160 точек по горизонтали, среднее разрешение - вдвое больше (320 точек), а высокое разрешение - ci:μe вдвое больще (640 точек). Число килобайт памяти, требуемое для каждого режима, приведено в [4.5.3 ]. В двух- и четырехцветном режимах PCjr имеет возможност1, выбрать любой из 16 доступных цветов. Цветной адаптер более • ограничен. В д,вухцветном режиме он всегда ограничен белым и черншм, а в четырехцветном режиме только цвет фона может выбираться из 16 цветов, в то время, как основной цвет ·ну_жно брать только из двух предопределенных палитр. Палитра О со,1.ср­ жит коричневый, зелен~й и красный цвета, а палитра ·1 - циан, магента и белый. • В отличие от текстовых данных в режимах 4-6 и 8-А графи­ ческие данные разбиты на видеостранице на части. В бол1,шинстве режимов данные разбиваются на две части, при этом первая поло­ вина буфера содержит данные для четных строк экрана, а вторая - для нечетных (строки нумеруются начиная с верха экрана вниз). Однако в 16-цветных режимах PCjr буфер размером 32К .делится на четыре части, каждая из которых содержит данные для каждой четвертой строки. . В 4-цветных режимах первый байт буфера содержит инфор­ мацию о самых левых точках строки О, причем старший бит отно-
Вывод на т·ерминал 229 сится к самой левой точке. Второй байт содержит информацию о следующем сегменте строки и т.д. Для всей строки требуется 80 байт. 81-й байт содержит информацию о левом конце строки 2. В 16-цветных режимах картина приблизительно такая же, но для каждой строки требуется 160 байт и каждая часть буфера содержит данные только для вдвое меньшего числа строк. Для цветного графического адаптера четные строки занимают память со смещениями от 0000 до IFЗFH, а нечетные - от 2000Н до ЗFЗFН. Промежуток между IFЗFH и 2000Н игнорируется. Для PCjr соответствующие ячейки могут существенно различаться в зависимости от режима и числа используемых страниц. PCjr специально устроен таким образом, что вывод в 16К, начина­ ющихся с сегмента В800Н, перенаправляется в ту область памяти, где реально расположен видеобуфер. Это свойство позволяет писать программы, которые будут одинаково работать на цветном дисплее и PCjr. . Для режимов экрана EGA от DH до • ОН память организована совсем по-другому. Она разделяется на одну, две или четыре бито­ вые плоскости, каждая из которых организована так же, как для черно-белого режима высокого разрешения, описанного выше: когда байт данных посылается в определенный адрес видеобуфера, каждый бит соqrветствует точке на экран'е, причем они описывают горизонтальный сегмент строки и бит 7 соответствует самой левой точке. Записываются четыре такие. битовые плоскости, соответ­ ствующие одним и тем же адресам, в видеобуфере. Это отводит каждой точке 4 бита, что позв~ляет описывать 16 цветов. На рис. 4.4 показаны различные схемы распределения памяти. В графическом режиме могут выводиться и символы. Qднако они создаются ·не обычным способом. BIOS вырисовывает их пото­ чечно, не изменяя фонового цвет.а. По этой причине такие вещи, как негативное изображение и мигание символов, недоступны в графическом режиме. Не выводится и курсор. BIOS может читать и определять установку точек в позиции курсора, чтобы узнать, какой символ там содержится. Символы располагаются в одной из • позиций,' соответствующих обычным строкам и столбцам. Это означает, что они всегда начинаются на границе, кратной восьми точкам. 4.4.1 . Установка цветов длЯ' точечной rрафнкн РСjг и EGA работают с цветом совсем по-другому, чем цветной адаптер. Они используют регистры палитры, которые позволяют в любой момент изменить цвет, соответствующий данному коду.
230 4 цвета (цветной адаптер) 16 цветов (PCjr) 4 цвета ~ (EGA с моно­ хромным дисплеем) 16 цветов Точки сканиру- емой строки Биты пам11ти Точки скани- руемой строки Биты пам11ти Точки скани­ руемой строки Биты пам11ти Точки скани­ руемой строки Биты пам11ти •Точки скани­ руемой строки Биты пам11ти (EGA с цветным дисплеем) -- 0000000000000000 ttt. t - - роооооо900000000 88000 88001 - - 000 00000~ ,l,~ -- 0000000000000000 ~...___, 88000 88001 - 00000000 ,t.. _ , ')::; • --00000~~ 88000 •88001 Глава 4 Последовательна11 органиэаци11 пам11ти Сканируемые строки располагаютс11 поочередно в отдельных блоках пам11ти Кажда11 четверта11 скани­ руема11 строка в отдельном блоке пам11тм Последовательна11 органиэаци11 пам11ти - 00000000 J&oooooo -00000000 Битова11 плоскость О Битова11 плоскость 2 АОООО (2 байта на каждый адрес) - 00000000 - !~000000 ,QДОООООО 00,000000 9,.0000000 АОООО Посnедоватеnьна11 органиэаци11 пам11ти Битова11 плоскость О Битова11 плоскость 1 Битова11 плоскость 2 Битова11 плоскость З (4 байта на каждый адрес) Рис.4.4. Четыре способа nредстаWiения 1·рафи•1еской 1111формацш1 11 памяти Вследствие этой разницы мы будем обсуждать обе системы отдельно и начнем с цветного адаптера.
Вывод на терминал 231 Обе системы используют один и тот же основной набор кодов цвета, который в точности совпадает с применяемым в текстовых режимах: НомеQ KOl!a • Цепочка битоu Цвет о 0000 черный 0001 СИIIИЙ 2 0010 зеленый 3 0011 ЦИЮI 4 0100 красный 5 0101 маге1па 6 0110 кор►1ч11е11ый 7 0111 белый 8 1000 серый 9 1001 11рко-сшшй 10 1010 ярко-зеленый 11 1011 11рК ►IЙ ЦШIII 12 1100 розовый 13 1101 ярк11я маrе1па 14 1110 желтый 15 1111 11рко-6елый Для цветного графического адаптера цвет доступен только в режиме умеренного разрешения. Для каждой точки отводятся два бита каждого байта видеобуфера. Четыре возможные комбинации этих битов представляют один фоновый и три основных цвета. Фоновый цвет может быть любым из 16. Однако три основных цвета могут выбираться из одной из двух щtлитр, каждая из которых содержит только три предопределенных цвета. Это следующие цвета: НомеQ KOl!a Цепочка битов 1 Пащпра О Палитра 1 о 00 ЦIICT фо1~а цnет фона 1 01 ЗCJICIIЫЙ ЦИаll 2 10 красный Mlll'CIITa 3 11 жслтый/корич11с111,1й 6еJ11,1Й Если вы в какой-то момент переключились между палитрами, то все выведенные на экран цвета будут соответственно изменены.
232 Глава 4 Единственный способ использовать цвет, не входящий в эти палитры, состоит в том, чтобы искусственно рассматривать один из цветов палитры как фоновый цвет, что предполагает заполнение этим цветом всего экрана, когда экран чистится (испол1,зуйте для этого прямое отображение в память). Истинный фоновый цвет может просматриваться "сквозь него" в качестве основного цвета. Такая техника приводит к созданию границы экрана, аналогичной той, что изображается в текстовых режимах. В противном случае граница экрана не может быть выделена цветом, так как весь экран закрашивается фоновым· цветом, хотя точки, относящиеся к области границы, нельзя адресовать. Отметим, что BIOS хранит в своей области данных однобайтовую переменную, которая содер­ жит текущий номер палитры. Ее адрес - 0040:0066Н. Изменение этого числа не меняет текущую установку палитры; наоборот, если вы измените цвет палитры другими средствами, помимо функций операционной системы, то значение этой переменной будет модифицировано. Символы могут перемешиваться с точечной графикой. Цвет, которым будут выводиться символы, зависит от того, какую функ­ цию вы предпочтете для их вывода. Простейшая функция по умолчанию использует третий цвет текущей палитры. Однако име­ ется ряд способов выбрать любой из цветов палитры, а также выводить символы различными цветами. Эти проблемы обсуж­ даются в [4.1 .3 ]. EGA и РСjг обеспечивают добавочную гибкость в работе с атрибутами цвета независимо от режима. При 16-цвстной графике четыре бита, находящиеся в памяти для каждой точки экрана, дают цепочку битов, которая не переводится прямо в соответ­ ствующие цвета приведенной таблицы. Вместо этого каждый номер относится к одному из 16 регистров палитры. Каждый из этих регистров содержит цепочку битов, соответствующую цвету, который будет выводиться на самом деле. Если вес 16 регистров будут содержать 0100, то независимо от того, какой атрибут приписан точке в памяти, она будет выведена красным цветом. Значение в регистре О используется в качестве фонового цвета. На рис. 4.1 в [4.1 .3] показан этот механизм. В двух- и четырехцветном режимах необходимы только первые два или четыре регистра палитры. Регистры палитры позволяют программе изменить цвет выво­ димого без каких-либо изменений в видеобуфере. Более того, отдельные объекты могут появляться и исчезать, как по волшеб­ ству. Это достигается изменением значения, содержащегося в регистре палитры, соответствующем данному объекту, на значение фонового цвета. Предположим, что фоновый цвет черный (0000) и что объект выведен с атрибутом 1110, так что он выводится в том
Вывод на терминал 233 цвете, который указан в регистре палитры 15 (по умолчанию значение для этого регистра - желтый). Если изменить значение регистра 15 на 0000 (черный фоновый цвет), то объект исчезнет. Но на самом деле объект хранится в памяти, так как он записан с атрибутом 1110, а не с атрибутом 0000, как вес точки фона. Объект может быть сделан снова видимым, если изменит~, значение регистра палитры 15 на 1110. Нс обязательно, чтобы исчезали все желтые объекты, поскольку некоторые могут быть выведены с другим атрибутом, который также соответствует регистру палитры, содержащему желтый цвет. - EGA может использовать 6, бит регистра палитры, а нс 4, когда к нему присоединен улучшенный цветной графический дисплей фирмы IВМ. При этом становятся доступными 64 цвета, кодировка для которых R'G'B'RGB. R, G и В соответствуют темным цветам, а R', G' и в· - светлым. Различные комбинации создают 64 отrенка. Как всегда, 111111 соответствует белому цвету, а 000000 - черному. Отметим, что через регистры палитры для EGA всегда доступны 64 цвета независимо от того, в каком режиме он рабо­ тает. При работе в режиме 4-цветной графики (как у цветного адаптера) активны только младшие 4 регистра· палитры, но они могут содержать любые цвета. Высокий уровень Когда цветной дисплей работает в графическом режиме, Бейсик обрабатывает оператор COLOR ·по-другому, чем в тексто­ вом режиме. Сначала идет фоновый цвет в виде числа от О до 15, а затем номер палитры О или 1. Н~шример, COLOR 2, 1 устанав­ ливает зеленый фоновый цвет ( #2) для всего экрана и активи­ зирует пал_итру 1. После этого три возможных основных цвета указываются их номерами в палитре: 1 - циан, 2 - магснта и 3 - белый (сравните с оператором PAINT). Чтобы выключит~, цвет в режиме умеренного разрешения, напишите SCREEN , 1. Отмстим, что использование только черного и белого цветов в режиме уме­ ренного разрешения не приводит к экономии памяти. PCjr исполь­ зует оператор COLOR таким образом только в режиме SCREEN 1. Для режимов от SCREEN 3 до SCREEN 6 формат этого оператора COLOR основной,фоновый. При этом основной цвет соответствует числувдиапазонеот1до15в16-цветномрежимеиотIдо3-в 4-цветном. Код цвета не должен быть равным О, это значение зарезервировано для фонового цвета. Существуют специальные операторы для установки регистров палитры: PALETTE и PALETTE USING. PALETTE vстанавливает цвет, соответствующий любому атрибуту. Напримёр, PALETTE 9, 11 приводит к тому, что точки, нарисованные с цветом палитры
234 Глава 4 9 (обычно светло-синий), будут выведены в цвете 11 (светлый циан)~ Чтобы изменить установку всех регистров палитры к их первоначальному значению, т.е. чтобы регистр О содержал О, регистр 12 - 12 и т.д., надо написать просто PALETTE. Отмстим, что в режимах SCREEN 4 и SCREEN 6 регистры палитры иници­ ализируются таким образом, чтобы атрибуты цветов 1-3 были такими же, как для палитры l на цветном графическом дисплее. Это делается в целях совместимости. Все 16 регистров палитры могут быть установлены одним опе-­ ратором PALETTE USING. PALETTE USING направляет содсг жимое 16-элементноrо целого массива в регистры палитры. Им~:я несколько таких массивов, программа может быстро переключать различные схемы цветов. Каждый элемент массива должен быть числом в диапазоне от О до 15 или -1 . В последнем слvчае соответ­ ствующий регистр не изменяется. Например, дл~ обращения привычной схемы цветов создайте массив, в котором ARRAYNAME(0) .. 15, ARRAYNAMEO) = 14 и т.д. Затем напи­ шите PALETTE USING ARRAYNAME(0) и содержимое массива ARRAYNAME будет передано в регистры палитры. О индицирует начальную позицию в массиве, с которой надо брат~, данные, посылаемые в регистры. Могут использоват1,ся более длинные массивы, из которых данные можно брать начиная с любой точки при условии, что до конца массива еще ест1, 16 ::>пемснтов. PALETTE USING ARRAYNAMEO2> будет брать данные начиная с 12-ro байта массива. Отметим, что оператор PALETTE USING работает как в текстовом, так и в графическом режимах. Вот пример: НЮ DEF INT A-Z 1sce переменные 11ет,1с 110 DIM SCHEMEl(l6) ·массш1д,11_и схемы ц11сто11 #1 120 DIM SCHEME2(16) ·масс•ш дли схемы ц11ето11 #2 130 DATA 3,5 ,9 ,2,4,12,15,1,6,7,I4,13,8,l l,I0 ,0 140 DATA 0,ll ,13,7,1 ,12 ,2 ,5 ,10,8 ,14,6 ,I5 .4,9 ,3 · 150FORN=ОТО15 ·дли каждого рсп1стр~ палитры 160 READ Q нро•1итап, код ц1~еп1 170 SCHEMEI (N) = Q и 1юмеспm, ею 11 масс•ш 180 NEXT 190FORN=ОТО15 200 READ Q 210 SCHEME2(N) = Q 220 NEXT 230 РАLЕТТЕ USING SCHEMEI (О) 500 РАLЕТТЕ USING SCHEME2(0) 'то же самое со нторым массн~юм ·уста~ю11ка рсrнстроп ·меняем их посреди программы
Вывод па термипал 235 Средний урове~ь Функция ВН прерывания l0H устанавливает как фоновый цвет, так и цвета палитры, но не одновременно. Для установки фонового цвета надо поместить в ВН О, а затем код цвета от О до 15 - в BL. Для установки палитры надо поместить в ВН 1, а в BL О или 1. В данном примере устанавливается цвет фона циан и выбирается палитра О: ;---уста~юока цвета фона и палитры моv лн.оnн ;функция уст::шо11ки цвета MOV BI-1,0 ;сначала устанавщшаем фо1ю11ый. ц~1ет MOV BL,3 ;код ц~1а~ш INT !ОН ;установка цвета MOV ВН,1 ;теперь устанавшшаем палитру MOV ·вL, 1 ;выбираем палитру 1 INT !ОН ;устанавливаем палитру На PCjr эта функция работает точно так •же в 4-цветном режиме, устанавливая регистры 1-3 в одну из схем цветов, исполь­ зуемых цветным адат:ером. В 2-цветном режиме О. в BL соответ­ ствует белому цвету, как цвету 1, а 1 - черному. Эта функция не влияет на назначения, испол1,зуемые в 16-цветном режиме. Однако во всех случаях фоновый цвет может быт1, установлен засылкой в ВН О, а в BL - кода цвета. Низкий уровень Для цветного адаптера мы можем получить доступ к "регистру выбора цвета" через порт 3O9Н. В графических режимах этот регистр действует по-другому, чем в текстовых (описанных в [4.1 .3 ]) . Биты 0-3 содержат информацию о фоновом цвете в обычном формате (соответственно синий, зеленый и красный компоненты и интенсивность). Бит 5 выбирает палитру, и когда ·этот бит равен О, то назначается палитра номер О. В графических режимах остальные биты не имеют значения. Этот регистр только для записи, поэтому вы должны указывать информацию и 6 фоновом цвете, и о палитре при· изменении любого из них. MOV DX,3D9H MOV ЛL,001001108 OUT DX,AL ;адрес рег~1стра выбора цвета ;цепочка битов для циана и палитры 1 ;посылаем ее Поскольку PCjr и EGA используют регистры палитры, данный пример к ним неприменим. Для них надо просто загрузить требу-
236 Глава 4 емые значения в эти регистры. У PCjr такие регистры нумеруются от l0H до lFH. Доступ ко всем регистрам осуществляется через один порт с адресом ЗDАН. Любое новое значение, принимаемое этим портом, воспринимается адресным регистром. Поэтому надо послать сначала номер регистра, а затем код цвета. Чтобы быть уверенным, что порт ожидает номер регистра, надо прочитать из него. В следующем примере ярко-синий цвет ( 1001) помещается в регистр палитры 2: ;---помещаем код· ярко-синего цвета в регистр палитры 2 MOV DX,ЗDAH ;адрес массива ворот дисплея IN AL,DX ;читаем из него MOV AL,l2H ;номер регистра OUT DX,AL ;посылаем номер регистра MOV ЛL,000010018 ;код ярко-сш1его цвета OUT DX,AL ;посылаем ц11ет У EGA адрес порта доступа к регистрам палитры - ЗСОН, а регистры нумеруются от 00 до 0FH. Надо прочитать из порта ЗDАН (а не ЗСОН), чтобы быть уверенным, что ожидается номер регистра. Когда к EGA присоединен улучшенный цветной дисплей и переключатели установлены соответствующим образом, в регистры помещаются 6-битовые значения. 4.4 .2. Рисование точки на экране (монохромный, цве.тной адаптеры и PCjr) Вследствие организации графической информации в видео­ буфере вывод одной точки подразумевает изменение отдельных битов памяти. Режимы двух, четырех и шестнадцати. цветов тре­ буют, чтобы для установки характеристик одной точки были изме­ нены один, два и четыре бита соответственно. Для этих операций необходимы огромные затраты процессорного времени, поэтому графическое программное обеспечение, как правило, работает очень медленно. Тщательное обдумывание часто позволяет сразу установить все биты одного байта, а не обращаться к одному и тому же байту 4 или 8 раз. Имейте это в виду и не следуйте слепо приведенной здесь технике поточечноrо вывода. Высокий уровень Бейсик предоставляет операторы PSET и PRESET для измене­ ния цвета отдельной точки. Эти имена образованы .от PointSET (установка точки) и PointRESET (сброс точки).· Они очень
Вывод на терминал 237 похожи. За ними должны следовать координаты столбца и строки, укi}зываемой точки, заключенные в скобки. Отметим, что коорди­ наты следуют в порядке х,у - т.е. сначала идет столбец, а затем строка; этот порядок обратный по отношению к порядку оператора LOCATE, который позиционирует текст на экране. PSET(S0,80) или PRESET(S0,80) vстанавлИ'Вают цвет точки в столбце 50 и строке 80. За оператором PSET может следовать код цвета, кото­ рый лежит в диапазоне, определяемом текущим режимом экрана. Если код цвета не указан, то берется максимальный номер кода, который допустим для данного режима. В PRESET цвет нс ук:;зы­ вается. Он всегда возвращает точке цвет фона. (код 0). Например: 100 PSET(I00,180),3 110 PRESET(I00,180) 'установка цвета 3 текущей палитры 'изменение цвета то•1ки на фоновый PSET и PRESET обычно используют систему координат, в ко­ торой левый верхний угол экра.на имеет ·координаты 0,0. Оператор WINDOW позволяет переопределить систему координат так, что, например, координаты левого верхнего угла будут 100,100, центра экрана 0,0, а правого нижнего угла - t00,-100. Для этого случая надо записать оператор в виде WINDOW<-100,100)-(100,-100). (Новые координаты не будут влиять на систему координат 25х80 (или 25х40), в которой оператор LOCA ТЕ позиционирует символы на графическом экране [4.2 .1 J.) Как и в операторе LINE [4.4 .5 ], первое число каждой пары в скобках указывает горизонтальную координату (по оси х). Коорди­ наты могут быть как положительными, так и отрицательными, лишь бы они не были равными. Левому краю экрана всегда прис­ ваивается меньшее число (которое ·моiет быть большим отрица­ тельным). Таким образом, даже если вы поменяете координаты в примере и запишете оператор WINDOW000,-100)-( -100,100), то значение -100 будет взято для левой границы экрана. Второе число каждой пары координат определяет границы экрана по вертикали. И опять мен1,шсе значсн~1с будет относип,ся • к нижней границе экрана независимо от того, в какой паре координат оно указано. Большее положительное значсн~1с (или меньшее· из двух отрицательных) присваивается в качестве значения оси у для верхней строки экрана. Направление увеличения значений может быть обращено, с тем чтобы максимальные значения соответствовали низу экрана и наоборот. Надо просто добавить к -оператору слово SCREEN, например WINDOW SCREEN(-100,'J00)-000,-100>. Программа может укащ,шать точки, относящиеся к области за пределами координат экрана. Например, центр окружности может находиться за пределами экрана, с тем чтобы видна была только
238 Глава 4 часть дуги. Отметим, что координаты, указываемые оператором WINDOW, могут непрерывно изменяться при изменении масштаба или угла зрения на объект. Изображение должно псрсрисо­ вываться, а иногда стираться, при изменении координат окна. Оператор РМАР преобразует координаты от обычной физичес­ кой системы координат к "мировой" системе, устанавливаемой оператором WINDOW. РМАР использует четыре кодовых номера: О преобразует х из "миропоi'I" системы в физическую 1 . преобразует у из "миро1юr1" с11стемы в физи•1~кую 2 преобразует х из ф11зичсской системы в "м11ропую'" •З преобрнзует у из ф11з~1•1сской с11стемы в "м11ропую" Оператор имеет форму РМАР(позиция,код). Предположим, что вы установили систему "мировых'' координат оператором WINDOW. Координаты левого верхнего угла экрана - ( -100,100), а правого нижнего - (100,-100). Какой будет позиция центральной точки экрана (0,0) при использовании обычной физической сис­ темы. 320х200, в которой левый верхний уrол имеет координаты 0,0? Чтобы найти Х, запишите Х "=' РМАР(О,0), а У - запишите У = РМАР(О,1). Вы получите значение Х = 160,аУ =100. Средний уровень Функция СН прерывания l0H устанавливает точку. DX содер-' жит строку, а. СХ - столбец. Они отсчитываются от О. Код цвета помещается в AL. Отметим, что содержимое АХ будет разрушено при выполнении прерывания. Если вы используете зто прерывание в цикле, то не забудьте сохранить АХ на стеке и каждый раз восстанавливать его. ;---вывод точки с координа·шми 100.180 MOV АН.ОСН MOV АL,З MOV СХ,100 MOV DX,180 INT !ОН ;---стираем то•1ку MOV ЛI-1,ОСН MOV AL,0 моv DX,100 ;фу11кцш1 уста~юнки точки ;ш.1бираем цвет З па;н-1тры ;строка ;столбец ;111,шодим точку ;11осста11а11ли11асм фу11кцию ;используем д.ня стира~1ш1 фо1юш,1й 1111ст ;строка
Вывод на терминал MOV СХ,180 INТ 101'1 ;столбец ;стираем точку 239 В то время как цвет палитры помещается в младшие биты AL, старший бит также имеет значение. Если он равен 1, то над цветом производится операция исключающего ИЛИ с текущим цветом. Напомним, что операция исключающего ИЛИ устанав­ ливает бит только в том случае, если из двух сравниваемых битов установлен только один. Если оба сравниваемые бита равны 1 или оба равны О, то результат будет О. Для двухцветного режима это означает, что такая операция обращает установку бита. Если эту операцию применить ко всем точкам экрана, то будет обращен весь экран. В 4- и 16-цветном режимах, с другой стороны, области экрана могут менять свои цвета. Например, пусть в 4-цветном режиме умеренного разрешения область ·занята точками либо цвета 1 палитры (установка битоJJ 01В) или цвета 2 палитры (10В). Что произойдет, если применить ко всем точкам этой области операцию исключающего ИЛИ с 11 В? О 1В перейдет в 1ОВ, а 10В перейдет в 01В - цвета_будут обращены. Низкий уровень На низкоt-1 уровне мы имеем возможность прямого доступа к видеобуферу (отображение в память). Сначала вы должны вычис­ лить смещение точки (а) внутри буфера и (б) внутри байта,, содер­ жащего биты, относящиеся к данной точке. После этого битовые операции обеспечат соответству19щую установку. Отметим, что если вы станете применять эту технику на PCjr, когда он работает в одном из 16-цветных режимов, использующих страницу разме­ ром 32К, то вывод в адреса, начинающиеся с параграфа В800Н, не будет перенаправлен ьерно. Вам необходимо прямо адресовать реальные ячейки, расположенные в сегменте ниже 2000Н. . Для нахождения точки нужно прежде всего определить, нахо­ : дится ли она в четной или нечетной строке. В данном примере строка помещена в СХ, а столбец - в DX. Если бит О регистра СХ равен О, то строка имеет четный номер. Четные строки распо­ ложены со смещением О относительно начала буфера. Если же строка имеет нечетный номер, то следует добавить смещение 2ОООН для указания на начало второй половины буфера. • Затем разделите номер строки на 2, подсчитайте число только четных или нечетных строк и умножьте результат на 80, так как на одну строку расходуется 80 байт. Для деления можно исполь­ ,зовать инструкцию SHL, а результат даст общее число байтов во
240 Глава 4 всех строках, предшествующих строке, в которой расположена искомая точка. Вместо того чтобы вычислять число столбцов в текущей строке, лучше сначала определить позицию пары битов в байте, которые содержат эту точку. Это достигается обращением всех битов в номере столбца (после того, как сохранена его копия) и выделе­ нием двух младших битов. Эта процедура покажет, находятся ли два бита, относящиеся к точке на первой, второй, третьей или чет­ вертой позиции, в байте. Умножив это значение на 2, мы полу­ чаем номер в байте первого из двух битов, относящихся к данной точке. Затем приходит время подсчитать число байтов в строке, пред­ шествующих байту, содержащему информацию о требуемой точке. Для режима умеренного разрешения надо разделить число столб­ цов на 4, а для высокого разрешения - на 8 . После этого надо сло­ жить три смещения: смещение за счет номера строки, за счет но­ мера столбца и смещение начала четных/ нечетных строк в буфе­ ре. После этого вы можете получить требуемый байт из буфера. Наконец, надо произвести операцию над соответствующими битами байта. Вращайте байт до тех пор, пока пара битов, отно­ сящихся к точке, не станет младшей. При вращении необходимо использовать ранее подсчитанное значение позиции битов. Затем выключите оба бита, поместив в них с помощью инструкции OR требуемый код палитры. Затем произведите обратное вращение и пошлите байт обратно в буфер. ;---u сегменте да1111ых PALCOLDB 2 ;---вызов процедуры MOV АХ,ОВ800Н MOV ES,AX MOV СХ,100 MOV DX,180 CALLSET DOT ;указыпаем ,щ шщсобуфер ;номер строки ;номер столбш1 ;---определяем число байтоо о предшестuующ11х строках SET DOTPROC TESTCL,l JZ EV ROW ;номер строки 11е•1ет111,1й'? ;есл11 11ет, то оперед MOV ВХ,2000Н ;смещение для нечетных строк JMP SHORT CONT ;переход uперед
Вывод на терминал EV _ROW: MOV ВХ,О CONT: SHR СХ, 1 MOV AL,80 ;смеще11ие для •1ет11ых строк ·;делим число строк на 2 ;ум11ожаем на 80 MUL CL ;в АХ - Чf1сло байтов ;---определяем положение пары бfп в байте MOV CX,DX • ;копируем номер столб1щ NOTCL• ;обращаем биты AND CL,000000118 ;в CL - nозицflЯ бfпов (0-3) SHL CL,l ;по~Ю\f1я nep11oro бf1та 1шры ;---rюдсчитьfuаем c~rщ:tt~1t: -сtо:Лбца II б:1йiJх SHR DX,l SHR DX,l ;дет1м номер столбца 11а 4 ;(нужны два младших бита) ;---вычисляем смещение для изменяемого байта ADD AX,DX ADD ВХ,АХ ;---изменяем биты нужного байта ;складываем все тpfl смещения MOV AH,ES:[BX) ;чfпаем 11уж111>1й байт ROR AH,CL ;сдош-аем 11уж111,1е биты 1111из AND AH,l l l l l l00B ;чистим м;шд1ш1е· 2 бита MOV AL,PAL_COL ;шменяем .flX на цnет палитры OR AH,,\L ROL AH,CL ;обратное пращение MOV ES: (ВХ) ,АН ;возвращаем байт RET SET_DOT ENDP 4.4.3. Рисование точки на экране (ЕGд) 241 У ЕGА-адаптера графика более сложная. С точк" зрения пpoIJ.FCCopa режимы экрана 0-7. действуют так же, как соответст­ ву-ющие режимы для цветного адаптера или PCjr, но режимы от DH до !ОН совершенно другие. Организация памяти для этих режимов меняется в зависимости от числа цветов и количества памяти, имеющейся на плате дисплея (см. рис. 4.4 .) В режимах D, Е и lOH память разбита на 4 битовые плоскости. Каждая плоскость организована таким же образом, как для черно­ белого режима высокого разрешения цветного адаптера, который обсуждался в [4.4 .2 ): когда байт данных посылается в опреде­ ленный адрес видеобуфера, каждый бит соответствует точке на экране, причем весь байт соответствует горизонтальному сегменту линии, а би:г 7 - самой левой точке. Выводятся четыре такие бито-
242 Глава 4 вые плоскости, относящиеся к одним и тем же адресам в видео­ буфере. Это приводит к тому, что каждая точка ош1с1,1вается четырьмя битами (давая 16 цветов), причем каждый бит находится в отдельном байте отдельной битовой плоскости. Но как вы можете записать 4 различных байта данных, распо­ ложенных по одному и тому же адресу? Ответ на :)ТОТ вопрос состоит в том, что вы не посылаете последовательно четыре байта по этому адресу. Вместо этого один из трех режимов записи позво­ ляет изменить все 4 байта на основании одного байта данных, полученного от процессора. Влияние данных, посланных процес­ сором, зависит от установки нескольких регистров, включающих два регистра маски, которые определяют, на какие биты и в к.1ких битовых плоскостях будут изменяться биты. Для понимания этих регистров мы должны сначала разобраться с четырьмя регистрами задвижки (latch register). Они содержат данные для четырех битовых плоскостей в той позиции, к которой было последнее обращение. (Заметим, что термин "битовая плос­ кость" используется как для целой области видеобуфера, так и для однобайтового буфера, временно хранящегося в регистре задвижки.) Когда процессор посылает данные по определенному адресу, эти данные могут изменить или полностью сменит~, данные регистра задвижки, а впоследствии именно данные 11з регистра задвижки записываются в видеобуфер. Каким образом данные процессора влияют на регистр задвижки, зависит от режима записи, а также от установки некоторых других регистров. При чтении адреса из видеобуфера регистры задвижки заполняются четырьмя байтами из четырех битовых плоскостей по данному адресу. Регистрами задвижки легко манипулировать, производя с их содержимым различные логические операции, что позволяет устраивать различные графические трюки. Регистр маски битов и регистр маски карты действуют на регистры задвижки, защищая определенные биты или битовые плоскости от изменения под действием данных, постvпающих от процессора. Регистр маски битов - это регистр тоm,ко· для записи, адрес порта которого ЗСFН. Сначала надо послать 8 в порт ЗСЕН, чтобы указать на этот регистр. Установка бита этого регистра в 1 маскирует этот бит во всех четырех битовых плоскостях, делая соответствующую точку недоступной для изменения. Однако поскольку оборудование работает в байтовых терминах, реал,,но "неизменяемые" биты перезаписыв.~ются в четыре битовые плос­ кости. Данные для этих маскируемых битов хранятся в регистрах задвижки, поэтому программа должна быть уверена, что текущее содержимое регистров задвижки относится к правил,,ному адресу памяти. По этой прич~1не перед записью по данному адресу надо считывать из него.
Вывод на терминал 243 Регистр маски карты имеет адрес порта 3С5Н. Этот регистр только для записи. Перед посылкой данных надо послать по этому адресу 2 как указатель. Биты 0-3 этого регистра соответствуют битовым плоскостям 0-3; старшие 4 бита регистра не исполь­ зуются. Когда биты 0-3 равны О, соответствующие битовые плос­ кости не изменяются при операциях записи. Это свойство исполь­ зуется по-разному в различных режимах записи, как вы увидите в дальнейшем. Три режима записи устанавливаются регистром режима, кото­ рый является регистром только для записи и который индексиру­ ется пре~варитсл;,ной засылко1i 5 в порт с адресом ЗСFН. Р~:ж11м записи устанавливается в битах О и 1, как число от О до 2. Би,: 2 должен быть равным О, так же как и биты 4-7 . Бит 3 устанав­ ливает один из двух режимов чтения из видеобуфера. Этот бит может быть О или 1. ВIOS EGA устанавливает режим записи в 00. Режим записи О В простейшем случае режим записи О копирует данные процес­ сора в каждую из четырех битовых плоскостей. Пусть, например, по определенному адресу видеобуфера послано 11111111 В и разре­ шены все биты и все битовые плоскости (т.е. ничто не маски­ ровано описанными выше регистрами масок). Тогда каждый бит во всех четырех плоскостях будет установле.н в 1, так что цепочка битов для каждой из соответствvющих точек бvдет 111 l В. Это означает, что 8 точек будут вывёдены в цвете 15, который изна­ чально соответствует ярко-белому цвету, хотя регистры палитры позволяют,- чтобы на самом деле это был любой из допустимых. цветов. Т~перь рассмотрим ту же ситуацию; когда посылается значение 00001000В. Цепочка битов для точки 3 будет 1111, а для остальных - 0000, что соответствует черному (изначаJiьно). Поэтому в данном случае только точка 3 появится на экране (ярко-белая), а остальные 7 точек будут выключены. Даже если остальные 7 точек перед этим выводились в каком-то цвете, теперь все они будут переключены на 0000. Рассмотрим другие цвета, кроме 1111 В. Если вы пошлете код палитры желаемого цвета в регистр маски карты, то регистр будет маскировать определенные битовые плоскости таким образом, что будет воспроизведен требуемый цвет. Например, если вы хотите цвет с кодом О 100, то пошлите О 100 в регистр маски карты. Тогда битовые плоскости О, 1 и 3 не будут изменяться. Когда вы пошлете по нужному адресу 11111111 В, это значение будет помещено только в би-:~:_овую плоскость 2, и цепочка битов для каждой точки будет О 100. Если вы пошлете по этому адресу
244 Глава 4 0000l000B, то точка 3 будет иметь цепочку битов 0100, а остальные точки - 0000 . Существует, однако, одна сложность. Реrистр маски карты запрещает изменение битовых плоскостей, но нс обнуляет их. Предположим, что битовая плоскостI, О была заполнена едини­ цами, а битовые плоскости I и 3 были заполнены нулями. Если вы запретите изменения в ::этих трех плоскостях, а затем пош,1етс llllllllB по определенному адресу, то битовая плоскосп, 2 будет заполнена 1111 l l l l В, а битовая плоскосп, О сохранит свои единицы, поэтому результирующий код цвета каждой точки станет 0lOlB. Встречаются случаи, коrда это свойство можно исполI,­ зовать для изменения цветов экрана. Но, вообще rоноря, необхо­ димо очищать все четыре битовые плоскости (т.с. вес четыре регистра задвижки) перед тем, как писать туда любые цвета, кроме 1111 В или ООООВ. Это делается просто посылкой О по ука­ занному адресу. Необходимо, чтобы при ::этом бы,,а разрешена запись во все четыре битовые плоскости. Приведенное выше обсуждение касалось одноврсмснноrо вывода восьми точек. Ну а как вынести мснI,шес ко,1ичсство точек? В этом случае, конечно, необходимо сохранить сущест­ вующие данные для некоторых точек, а чтобы ::это было возможно, текущее содержимое данноrо адреса сохранястсsI в рспктрах задвижки. Затем используется рсrистр маски битов длsI маски­ рования тех точек, которые нс должны измснят1,сs1. Если бит ::этоrо регистра сброшен в О, то данные, получаемые от процессора для этого бита, игнорируются и вместо них исполI,зуются данные, хранящиеся в регистрах задвижки. Равен ли :пот б11т в данных процессора О или 1 - нс имеет значения; если вы изменsIете тол,,ко бит 2, а вес остал1,ныс маскированы, то данные, которые приходят от процессора, моrут быть 0FFH или 4Н, ил11 любое другое значе­ ние, для котороrо бит 2 установлен. Если б11т 2 сброшен, то О помещается в ::этои позиции во всех разрешенных битовых плоскостях. Вообще rоворя, проrрамма должна сначала прочитап, ,,юбую ячейку, в которую она собирас;тся зап11еап, менI,ше 8 точек. Имеются два режима чтения (обсуждаемые в 14.4.41) ~1 безраз­ лично, какой из них выбран. Опсрациs, чтешнI загружает реп,стры задвижки четырьмя байтами данных для 1-1ужного адреса памsIп1. Данные, возвращаемые процессору опсрац11еii чтсн1нI, могут быт,, отброшены. До сих пор были рассмотрены самые IIростые возможности режима записи О. При жслан1111 вы можете дежIп, намного бо,,се сложные манипуляции. Одна из нозможностсii состоит н r,.10д11qт­ кации реrистров задвижки с помощI,ю логических операциii перед
Вывод на терминал 245 записью. Для реализации этой возможности регистр вращения дан-· ных использует следующие биты: биты 2-0 число враще11ий 4-3 00 данные не модифицируются 01 логическое И с регистром задвижки 10 логи•1еское ИЛИ с регистром задnижки 11 исклю'lающее ИЛИ с регистром задnижкн 1-5 IIC ИСll(J,JJ,Зуются Число вращений, которое .может быть от О до 7, показывает, сколько битов данных должны вращаться перед тем, как поместить их в регистр задвижки. Обычно это значение равно нулю. Анало­ гично биты 4-3, как правило, равны 00, кроме случаев, когда производятся логические опе1)3ции. За счет манипуляций с этим реmстром одни и те же данные могут выдавать различные цвета и изображения без дополнительной процессорной обработки. Регистр вращения данных индексируется посылкой 3 в порт ЗСЕН; затем данные посылаются в ЗСFН. • Наконец, режим записи О может работать совсем по-другому, если разрешены установка/сброс. В этом случае определенн1>1е цвета в младших четырех битах регистра установки/ сброса (который тоже имеет адрес порта ЗСFН, а индексируется посылкой О в ЗСЕН). Имеется соответствующий регистр разрешения уста­ новки/ с~роса, который разрешает любой из этих четырех битов, устщ,авливая свои младшие биты в 1. Когда все 4 бита в регистре установки/ сброса разрешены,- они помещаются во· все 8 адресов битовой плоскости при получении данных от процессора, при этом сами данные процессора отбрасываются. Если разрешены не вес биты установки/сброса, то данные процессора помещаются для запрещенных точек. •Отметим; что регистр маски битов запрещает запись данных установки/сброса в определенные точки, но ~ста­ новка регистра маски карты игнорируется при использовании ·установки/ сброса. BIOS инициализирует регистр разрешения установки/сброса в О, так что он неактивен. Его адрес порта ЗСFН, а индексируется он посылкой I в порт ЗСЕН. Режим записи Режим записи ·предназначен для специальных приложений. В этом режиме текущее содержимое регистра задвижки записывается по указанному адресу. Напоминаем, что регистры задвижки запол­ няются операцией чтения. Этот режим очень полезен для быстрого
246 Глава 4 переноса данных при операциях сдвига экрана. Регистр маски битов и регистр маски карты не влияют на эту операцию. Нс имеет значения также, какие данные посылает процессор - содер­ жимое регистров задвижки записывается в память без изменений. 3 2 Вмдеобуфер г------ 1 117 11 ... - - -- - Регистр маски о о о о битов , о о о о о о оо о о, - - --, 1 71 1 --- ..J 1 1 1 1 1 1 1 1 оо 1 1о АООО:0002 АООО:0001 АООО:0000 Не измен11етс11 Регистры задвижки Регистр маски карты Регистры разреwени11 установки/сброса 321О -3--2 ___ __, о ///l. Регистр 10110 установки/сброса • , ______ _, ~ 10111101В 321о Режим вывода О Рис. 4.5. Графические режимы ш,шодn ЕGЛ Режим записи 2 Данные от процессора Режим записи 2 предоставляет альтернативный способ уста­ новки отдельных точек. Процессор посылает данные, у которых имеют значение только 4 младших бита, рассматриваемые ка~ цвет (индекс регистра палитры). Можно сказать, что эта цепочка
Вывод на термина.л 2 Регистр , маски битов о ВИАеобуфер АООО:0002 АООО:0001 АООО:0000 Регистры задвижки > Режим вывода 1 Битовые ПIIОСКОСТИ э2 АООО:0002 АООО:0001 .АООО:0000 АООО:0052 АООО:0051 АООО:0050 г------ 1 1 1 11 11 L1 - 1 1Оста~оте11 неизменными 1 1 -' О 11о Регистры задвижки Г1 11 11 1 L---- 11 j Оста~отс11 неизменными ... 1 .1 Регистр маски карты э 2.().1 О . U 000011018 Данные от процессора Режим ВЫВОАI 2 Рис. 4.5. Грnфнческие режимы вывода ЕGЛ lпродо11ж~11ис) 247 битов вставляется поперек битовых плоскостей. Цепочка дубли­ руется на все восемь точек, относящихся к данному адресу, до тех пор, пока •регистр маски битов не предохраняет определенные
248 Глава 4 точки от изменения. Регистр маски карты пктивен, как и в режиме записи О. Конечно, процессор должен послать полный байт, но только младшие 4 бита существенны. Высокий уровень / Бейсик поддерживает EGA в традиционных режимах цветного графического адаптера. Ко времени выхода этой книги поддержки дополнительных режимов EGA не существовало. Поэтому у вас нет другого выхода, кроме как использовать прямое отобраИ<ение в видеобуфер, который начинается с адреса АООО:0000. Самая тяжелая проблема состоит в установке режима дисплея. Для ее решения используйте следующую процедуру на машинном языке: 10 S$ = CHR$(&H2A) + CHR$(&HE4) + CHR$(&H80) + CIIR$<&H0D) + CHR$(&HCD) + СНR$<&Н10) + СНR$(&НСВ) 20 DEF SEG ·установка сегмента 30 У = VARPТR(S$) ·указател1, на строку 40 Z = РЕЕК (У + 1) + РЕЕК <У + 2) *256 вы•1нсление адреса строк~• 50 CALL Z вызон процедуры Четвертый байт S$ содержит номер режима, в данном случае режим D. Вы можете выбрать другой режим. В приложении r объ­ ясняется, как, эта процедура работает в Бейсике. Она полност1,ю завершенная, не нужно никакой побочной памяти, в которой содержался бы машинный код. Не забудьте восстановить режим дисплея после завершения своих манипуляций. Затем надо установить соответствующий режим записи. Вот как устанавливается режим записи 2: 50 ОUТ &Н3СЕ,5 60 OUT &H3CF,2 ·индексируем регистр режима записи ·выбираем режим 2 Режим записи также должен быть восстановлен после завершения программы. Наконец, приведем образцы кода, реализующие прямое отображение в видеобуфер: Режим записи О: 100 ·рисуем красную точку в левом верхнем углу экрана 110 DEF SEG = &НАООО ·указыв.,ем на нидеобуфер 120 OUT &Н3СЕ,8 ·адресуем регистр маски битов
Вывод на терминал 130 OUT &H3CF,128 140 Х = РЕЕК(О) 150 РОКЕ 0,0 160 OUT &Н3С4,2 170 OUT &Н3С5,4 180 РОКЕ 0,&HFF Режим записи 1: ·маск~1руем 11сс бить,, кроме се111,мою ·•штаем текущее з11ачеш1с 11 зню1ижку чистим ндрссуем рс1·~1стр маек~• карт1,1 ·уста~~авщш.аем крнс111,1й цвет 'рисуем то•1ку 100 ·копируем.верхнюю строчку точек в следующую ll0 DEF SEG = &НАООО ·указываем на видеобуфер 120FORN=ОТО79 'для всех 80 б..1йт строки 130 Х = PEEK(N) 140 РОКЕ N + 80,У 150 NEXT .Ре~им записи 2: ·з.1полняем задвижки копируем в следующую строку ·переходим к следующему сегменту 100 'рисуем красную точку в левом верхнем углу экрана 110 DEF SEG = &НАООО ·указыв.1ем на видеобуфер 120 OUT &Н3СЕ,8 130 OUT &H3CF,128 140 Х = РЕЕК(О) 150 РОКЕ 0,4 Средний уровень ·адресуем регистр маек~• битов ·маскируем все биты, кроме сед1,мого ·читаем текущее з1~а•1еш1е в зад11ижку ·посылаем красный ц~~ет 249 EGA поддерживает стандартные графические функции BIOS. Можно вывести точку с помощью функции СН прерывания IOH, так ж1r как для цветного дисплея или PCjr. При входе DX должен содержать номер строки, а СХ - номер столбца, и то и другое отсчитывается от О. Код цвета помещается в AL. Содержимое АХ меняется при вьшолнен~и прерывания. ;---рисуем точку по адресу 50,100 MOV АН,ОСН ;функция вывода точм1 MOV AL,12 ;выбираем регистр палитры 12 MOV СХ,100 MOV DX,50 INT l0H ;номер строки ;номер столбца ;рисуем точку
250 Глава 4 Низкий уровень Ниже приведены примеры для трех режимов записи. Перед их выполнением необходимо установит~, режим дисплея, 11спол1,­ зующий видеобуфер с адреса АООО:0000. Для.3тоrо можно приме­ нить стандартную функцию BIOS, например, для установк11 режима D: MOV АН.О MOV ЛL,0DH INT I0H ;фу11к11ш1 уста11011кн режима ;пыбнрасм peжflM D ;yCTUIШIIJIИЩICM режим Не забудьте восстановить режим перед завершением программы. Кроме того, необходимо установить требуем1>1й режим записи. Вот пример установки режима записи 2: MOV DХ,ЗСЕН ;указь111,1ем ,ш ре,·истр адреса MOV AL,S ;и11дскснрусм рсп,стр 5 OUT DX,AL ;11oc1,IJШCM шщекс 'INC DX ;укnзь111:1ем 1ш ре1·►1стр реж~,ма MOV AL,2 ;111,1б►1расм режим зшшс~, 2 OUT DX,AL ;уста11а1uш1111е,м режим И наконец,. примеры трех режимов записи: Режим записи ,О: ;---рисуем красную точку в левом верхнем yrJ1y экр1111а MOV АХ,ОАОООН ;указываем на 1шдеобуфср MOV ES,AX _моv ох.о ;ук11зыщ1ем 1111 11ср111,1й байт буфера ;---маскftруем все биты, кроме сед1,мою MOV DХ,ЗСЕН ;указыпаем 1ш 11дрес11ый регистр моv AL,8 ;номер ре1·f1стра OUT DX,AL ;посылаем его INC DX ;указываем 11а рс1·истр д111111ых моv AL, 10000000В ;маска оuт DX,AL ;посылаем данные ;---чистим текущее содержимое задвижки MOV AL,ES: [ВХ) ;читаем содерж ►1мое в зад~,ижку MOV AL,0 MOV ES: [BXJ ,AI . ;готовимся к очнстке ;чистим з~щвижку
Вывод на терминал ;---установка регистра маски карты для красноl'О цвета моv DX,3C4H моv AL,2 оuт DX,AL INC DX MOV AL.4 OUT DX,AL ;---рисуем точку MOV AL,OFFH MOV ES: (ВХ] ;AL Режим записи 1: ;указываем 11а адресный раистр ;индекс регистра маек~• карты ;уста1-1овка адреса ;указываем 11а реr~1стр щ111111,1х • ;код цnета ;посылаем код •~вета ;любое з11а•1еш1е с уста~ювле1111i.1м 7 б~пом ;выводим точку ;---копируем строку в следующую строку MOV СХ,80 ;ч~1сло байтов 11 строке MOV ВХ,О ;начинаем с 1-го байта буфера моv моv NEXT: MOV АХ,ОАОООН ES,AX AL,ES:[BX) ;адрес буфера ;за1юл1111ем з:щвижку MOV ES: [ВХ] + 80,AL ;вьшод~1м 11 следующую строку INC вх LOOP NEXT; Режим записи 2: ;перехощ1м к сдедующему байту ;_---рисуем красную точку в левом верхнем углу экрана MOV АХ,ОАОООН ;адрес буфера MOV ES,AX MOV ВХ,О ;указываем на первi,1й байт буфера ;---установка регистра маски битов MOV DХ,ЗСЕН ;указываем 1ш адресный регистр MOV AL,8 ;регистр маек-и битов OUT DX,AL ;адресуем рег~1стр •INC DX ;указываем на реn1стр данных MOV AL,100000008 - ;маскируем все биты, кроме 7-ro OUT DX,AL ;посылаем данiн,,е ;---рисуем красную точку MOV AL,ES: (ВХ] ;заполняем регистры задвижк~• MOV AL,4 ;красный ц11ст MOV ES: [ВХ] ,AL ;р~1суем то•1ку 251
252 Глава 4 4.4 .4. Определение цвета точки экрана Для графических режимов цветного адаптера или PCjr опреде­ ление цвета точки на низком уровне состоит в обращении проце­ дуры вывода точки: программа читает из видеобуфера и выделяет интересующие биты. Однако для EGA этот метод непригоден, поскольку в режимах DH - l0H каждому адресу памяти соответ­ ствует два или четыре байта. EGA имеет два режима чтения, чтобы преодолеть эту трудность. Имейте в виду, что для PCjr и EGA после того, как вы определили код цвета точки, необходимо еще проверить установку текущего регистра палитры для этого кода, чтобы определить, какой цвет ему приписан. Любой язык программирования имеет доступ к двум режимам чтения EGA. В режиме О возвращается байт, содержащийся во всех четырех битовых плоскостях, по указанному адресу. Режим 1 ищет указанный код цвета и возвращает байт, в котором бит установлен в 1, когда соответствующая точка имеет данный цвет. Бит 3 регистра режима определяет, какой режим чтения уста­ новлен (О • режим О). Доступ к этому регистру осуществляется через порт ЗСFН, и вы должны предварительно пос.пать 5 в порт ЗСЕН, чтобы выбрать этот регистр. Обычно все остаю,ные биты этого регистра, который можно только писат1,, сброшены в О, кроме битов О и 1, которые определяют режим записи. Поскольку при инициализации BIOS устанавливает эти б!-fты в режим записи О (так что они оба равны 0), вам нужно просто послать в :этот регистр О, чтобы установить реж!fМ чтения О, и послать 8, чтобы установить режим чтения 1. Режим чтения О требует, чтобы вы предварительно установили регистр выбора карты. Единственная задача этого регистра - уста­ новить, какая из карт битов должна быть прочитана. Поэтому в неrо надо послать число от О до 3. Этот регистр имеет адрес порта ЗСFН и надо предварительно послать 4 в порт ЗСЕН, чтобы указать этот регистр. Режим чтения l более сложен. Сначала регистр сравнения цветов должен быть заполнен цепочкой битов- для кода цвета, который вы ищете. Этот код помещается в младшие 4 бита регистра; старшие 4 бита несущественны. Этот регистр имеет адрес порта ЗСFН и· указывается предварительной засылкой 2 в порт ЗСЕН. После чтения ячейки памяти возвращается байт, который имеет биты, установленные в 1 для каждой точки, имеющей нужный цвет. Однако за счет использования регистра безразличия цвета (color don't care register) один или более битов кода цвета могут при сравнении игнорироваться. Обычно 4 младших бита этого регистра установлены в 1; обнуление одного из этих битов приведет к тому, что содержимое соответствующей битовой плос-
Вывод на терминал 253 кости будет игнорироваться. Например, если цепочка битов для точки З (бит 3) по указанному адресу равна О 11 О и регистр срав­ нения цветов содержит значение 0010, то при сравнении будет возвращен байт, у которого бит 3 равен О, если в регистре безраз­ личия цветов все биты равны 1. Но если регистр безразличия цветов содержит 1011, то в байте, возвращаемом процессору,' бит З будет равен 1. . Регистр безразличия цветов имеет адрес порта ЗСFН и индек­ сируется засылкой 7 в порт ЗСЕН. Старшие 4 его бита не играют никакой роли. Отметим, что документация IBM (от 2 августа 1984 г.) утверждает, что регистр действует обратным образом, т.с. что I в регистре заставляет операцию сравнения иrнориров~ть соответствующую битовую плоскость. Эксперимент показывает обратное. • Ни один из этих двух режимов чтения не может дать быстрый ответ на вопрос о цвете определенной точки. В режиме чтения О необходимы 4 отдельных чтения, по одному для каждой битовой плоскости, после чеrс надо еще выделить соответствующие биты из каждого байта. В режиме чтения l, с другой стороны, может потребоваться до 16 чтений, прежде чем для требуемой точки . будет возвращен установленный бит, указывающий, что :Jта точка' имеет данный цвет. Хотя EGA относительно медленно выполняет данную задачу, с другими проблемами он справляется очень быстро. Высокий уровень Бейсик предоставляет функцию POINT, которая возвращает цвет точки. Цвет палитры точки, находящейся в столбце 200 и строке 100, находится путем Q = POINT(200, 100). Значение, возвращаемое в Q, это обычнь1й кодовый номер цвета. Если указана точка, находящаяся за предсщ1ми :Jкрана, то функция POINT возвращает значение -1. Когда координатная система экрана изменяется оператором WINDOW 14.4 .21, функция POINT переходит к новой системе.' POINT может также сообщит~, позицию последней выведенной точки. При использовании обычной координатной систеr.iы, в которой 0,0 соответствует левому верхнему углу :Jкрана, Q = POINT(I) возвращает в Q х-координату точки, а Q = POINT(2) - у-координату. Если действует оператор WINDOW, то Q POINT(З) и Q = POINT(4) возвращают х- и у-координаты в новой системе. Когда ·нет активного оператора WINDOW, послед­ ние два оператора действуют так же, как и первые два. К моменту выхода этой книги Бсikик нс поддерживал улуч­ шенные графические режимы EGA (D-I0H). В :Jтих режимах проr-
254 Глава 4 рамма должна прямо читать содержимое видеобуфера. Вот пример использования режима чтения 1 для поиска кодов цветов ООО 1 и 1001: 100 OUT &Н3СЕ,5 110 OUT &НЗСF,8 120 OUT &НЗСЕ,2 130 OUT &НЗСF,1 ·адрес регистра режима ·устанаwтваем режим чте1:ия О адрес регистра срав11е1шя цветов •ищем цвет ООО 1 140 OUT &НЗСЕ,7 ·адрес регистра безразличия·цветов 150 OUT &НЗСF,7 ·7 = 01118, поэтому ищем 0001 и 1001 160 DEF SEG = &НАООО ·адрес видеобуфера для ЕGЛ 170 Х = РЕЕК(О.) ·читаем первый байт 180 IF Х <> О THEN... •.. то цвет 0001 или 1001· найден Средний уро11ень Функция D прерывания lOH возвращает код цвета указанной точки.-ВIОS, имеющийся на плате EGA, обеспечивает работу жой функции в любом режиме дисплея. Надо поместить номер строки (отсчитываемый от О) в DX, а номер ст')лбца (также отсчиты­ ваемый от О) - в СХ. ·Результат возвращается в AL. ;---определяем код пащ,тры то•1ки 100,200 MOV ЛН,ОDН MOV DX,100 MOV СХ,200 INT !ОН Низкий уровень ;номер фу11к1\ш1 •1тсшн1 ,щста то•1к11 ;1юмер стр<ж11 ;1юмер столб,щ ;теперь код 10,ста II Лl, Для графических режимов цветного адаптера и PCjr надо просто обратить процесс прямого отображения в памят1,, которым устанавливается цвет точки, как показано в [4.4.2 ]. Можно использовать приведенный там пример, который надо завершить сл~дующим кодом: ;---11зме11еш1е бито11 ,место J\.11" вста11ки измс11еш1й> MOV А! l,ES: [ВХ] ROR ЛН,СL AND ЛН,00000011 В RET ;берем байт 11з 11уж1юй 1юз1щии ;сд1111i·асм 2 11уж111,1х б11та 11ш1з ;Ш,IКJ110 1 ШСМ оста;11,111.1с 65-IТЫ ;теперь в ЛН - к<щ нашпры
Вывод 11а термииал 255 Для режимов EGA DH - IOH надо пользов.1ться рсгистр.1ми, описанными выше. В следующем примере режим чтения О исполь­ зуется для чтения битовой плоскости 2 по адресу АООО:00 J2. ;---установка режима чтения MOV DХ,ЗСЕН ;индексный реrистр MOV AL.5 • ;с1~ачала адресуем реrистр режима ОUТ DX,AL ;посылаем и11декс INC DX ;указынаем на сам ре1·щ.-тр MOV AL,0 OUT DX,AL ;устанаwшнаем режш" •1те1ш11 О ;---установка битовой плоскости, которую будем читать DEC DX ;11азад к и11декс11ому реrистру MOV AL,4 ;адрес perиcrpa выбора карты OUT DX,AL ;посылаем и11декс INC DX ;указываем ,ш сам pentcтp MOV AL,2 ;запрос битовой плоскости 2 ОUТ DX,AL ;посылаем зна•1ение ;---чтение битовой плоскости MOV АХ,ОАОООН ;адрес видеобуфера MOV BS,AX ·моv вх,12 MOV AL,ES: (BXJ . ;смещение в буфере :•штаем из б•поuой 11,1оск(х.-ти 2 И '"аконец, пример поиска кодов цвета 001 О и IО IО с использованием режима чтения 1: ;---уста11овка режима •1те1ш11 MOV ох;зсвн ;perиcrp •шдекса MOV Al,,5 ;адресуем с11а 11ала ре,·истр режима OUT DX,AL ;посылаем и11декс INC DX ;указываем 11а сам регистр • MOV AL.8 ;устанамиваем бит 3 д.1111 режима 1 ОUТ DX,AL :уста11аапиваем режич ;---установка реrистра сра1111е11ия цветов DEC DX ;возвращаемся к и1щскс1ю~чу ре1-.1стру ;адрес реn-,стра срt1н11еш111 ц1~•т0t1 ;посылаем и11декс ;указы1111см 11а сам реt'ИСТР ;код цвета MOV OUT INC MOV OUT AL,2 DX,AL. DX AL.0010B DX,AL ;11осьu1ае~ч код ;---установка реrистра безразли 1ш11 цнс1·ов DEC DX ;возвращаемся к •••щекс,юму реn-,стру моv AL, 7 ;адрес реmстра безраЗJIИЧИИ ЩIСТОВ OUT DX,AL ;посылаем и11декс
256 INC DX MOV AL,01118 OUT DX,AL ;---поиск цвета MOV АХ,ОАОООН MOV ES,AX MOV ВХ,12 MOV AL,ES: [ВХ] СМР AL,0 JNZ FOUND 1Т ;указываем 11а сам реп1стр ;принимаем код1.1 1010 ищ1 001() ;посылаем да1111ые ;адрес 11н:1собуфсра ;смеще1те в буфере ;читаем позицftЮ буфера ;установле11ы биты? ;если да, ·го 11щем, у какой то•1кf1 / 4.4 .5 . Рисование nинин на экране ), Глава 4 Простейший способ нарисовать линию на экране состоит в том, чтобы вычислить следующую точку этой линии и изменить биты соответствующего байта. Такие операции очень медленны, хотя иногда их нельзя избежать. Если это возможно, то лvчшс вычис­ лить область точек экрана, которые имеют одинаковый цвет. Тогда требуемые операции над битами можно проделать тол1,ко над одним байтом, а затем этот байт может быть помещен в облает,, соответствующих позиций видеобуфера. Высокий уровень Бейсик позволяет рисовать прямые линии с помощ1,ю опера-_ тора LINE. LINE (20,10)-(40,30) рисует линию от столбца 20 и. строки 10 к столбцу 40 и строке 30. И строки, и столбцы нуме­ руются от нуля. Вы можете опустить координаты первой точки, в этом случае линия будет начинаться с последней точки, которая была ранее выведена графическим оператором. Вторая пара коор­ динат может задаваться также относитсл~,но первой с помощ1,ю конструкции ЦNЕ -STEP(xoffsct,yoffsct). Оператор LINE может укг-зывать также цвет и стил~, линии, Код цвета следует сразу за списком координат; LINE (50,50)- (60,60),2 выводит линию цве<гом 2. Когда цвет нс vказан, по умолчанию берется цвет 3. Возможност1, выбора стил~, ,;инии пред­ полагает указание чередования се точек. Образец может быт•~ представлен как в десятичной, так и в шестнадцатиричной форме.' Например, образец l0I0I0I0l0l0I0I0, который соответствус-i &НАААА, дает линию, точки которой имеют по очереди. данный цвет и фоновый. Стиль линии определяется трст1,им параметром после координат. Например, LINE (30,30Н40,40>,3,,&НАААА выводит линию с указанным стилем цветом 3.
,, Вывод иа терJ.1шшл Бейсик дает возможность также выполнят,, процедуры для рисования пр_ямоуrольников и окружностей. Прямоуrот,,ники выводятся с помощью оператора LINE. В данном с,1учас· коор­ динаты должны описыват,, левый верхний и правый нижний углы рамки. Надо просто указать В (Ьох, т.е. рамка) в качестве второго параметра за координатами. LINE (50,50)-(100,100),1,В,&НАААА рисует квадрат со стороной в 50 точек цветом 1 палитры, используя описанный выше метод. Для вывода прl!моуrольника, заполненного определенным цветом, надо использовать параметр BF (при этом стиль линии указывать нс надо). Окружности рисуются оператором CIRCLE. Их вывод основы­ вается на формуле CIRCLE (х,у),r,цвет,нач-уrол,кон-уrол,аспект. Координаты х,у дают адрес центра окружности н,I :Ji<-paнe, а r - радиус окружности в точках; вся остальная информация необя­ зательна. Цвет - :Jто код цвета, который по умолчанию берется равным 3. Если необходимо вывести тот,ко дугу окружности, то можно указать нач-уrо,1 и кон-уrол (когда они опущены, выво­ дится целая окружность). Углы измеряются как положительные или отрицательные величины, отсчитываемые от направления по горизонтали вправо. Они измеряются в радианах (в 360 градусах содержится 6,292 радиан, а один градус = 0,0174532 радиан). Аспект - это отношение горизонтальных и вертикальных размеров. Окружность получается на дисплее правит,ной формы, когда вы зададите аспект равным 5 / 6 для умеренного разрешения и 5 / 12 для высокоrо. Меньшие значения приводят к :Jллипсам, вi,Iтянутым по горизонтали, а большие - к вытянутым по ·вертикал'и. Для примера PI = 3,14159: CIRCLE<200,50),30,2,PI/2,PI,6 выводит дугу, центр которой находится в точке 50,200 с радиусом :за.точек цветом 2, причем будет выведен только левый верхний квадрант вертикально вытянутоrо :Jллипса. • , Более сложные линии могут выводиться с ПОМОЩl,Ю оператора DRAW, который необычайно гибок. За оператором DRAW следует строка (заключенная в скобки), в которой закодирована последо­ вательность ориентаций и длин сегментов, составляющих линию. Например, DRAW , "El2Fl2Gl2Hl2" выводит бубновую масть. Начальная точка устанавливается оператором PSET (обсуждаемым в [4.4 .2 }) ; в щхnивном случае по умолчанию , берется центр экрана. Основные коды состоят из буквы, за которой- следует длина сегмента в точках. Коды следующие: L!x 1шерх (11а х точ~к) Dx Вftl-13 Rx 1111ршю Lx uлево, 9 р Джордейн
258 Ех Fx Gx Нх IIO Дflal1JIШJIH IIIIC[JX Н 11111).1110 по Д5-Hl.l'OllaJIH ВIIИЗ "1 внраво 110 диаrоваJI"I шн1з .,. вл~во по диаГОIШJН·I IШCJ)X ~, BJ IC BO Глава 4 При умеренном разрешении l 00 точек по rоризон-тали и 100 точек по вертикали дают отрезки примерно одинаковой длины (на самом деле отношение у к х равно 5/6). При высоком разрешении горизонтальная линия будет приблизительно вдвое менr,ше, чем вертикальная. Из-за большего расстояния между точками диаго­ наль прямоугольника содержит ровно столько же точек, скол1,ко и максимальная сторона прямоуrолr,ника, хотя сам отрезок длиннее. Для рисования диагоналей с углами, отличными от 45° , используется кодона~ буква М. Этот код рисует следующ1111 сегмент линии в абсолютную или относительную позицию экрана. Чтобы указать абсолютную позицию, надо указать координаты х и у. DRAW "MS0,60" проведет линию в точку, имеющую коорди­ наты столбца 50 и строки 60. Для указания относителr,ных коор­ динат добавьте знаки + или - перед числами. Если текущее значение координаты х равно 100, то + 50 продолжит линию до столбца 150, ,а -50 - до столбца 50. Чтобы сдвинутr,оr из 100,100 в 120,70, напишите DRAW "М + 20,-30". Линия не обязательно будет непрерывной. Когда перед кодом стоит буква В, указатель перемещается заданным образом, но сегмент линии при этом не рисуется. Напрнмер, DR А W "LIOBU5Rl0" рисует две параллельные rоризонтат,ные линии. Чтобы из одной точки' начиналось несколr,ко сегментов, надо задать перед кодом букву N. В этом случае указателr, будет возвращаться в начальную точку после вывода сегмента. Существует ряд специальных кодов, которые, будучи помещен­ ными внутри строки, действуют на вес последующие коды (пока следующий аналогичный код не задаст другое действие). Цвет сегмента линии устанавливается буквой С, за которой следует код цвета. DRAW "C2D5" рисует линию, направленную вниз, цветом 2. Установка масштабного фактора меняет масштаб, в котором будет выводиться фигура или ее часть. Надо добавить к строке букву S, за которой следует фактор. Фактор - это число, которое для получения масштаба делится на 4. Обычно фактор ранен 4, что соответствует масштабу 1: l. Изменение фактора на 8 приведет к тому, что размер выводимой фигуры будет вдвое болr,шс. Для этого напишите DRAW "S8Ul2D12" и т.д. с помощью одного из двух к'одов вы можете вращап, оси коор­ динатной системы. Кодовая буква А еращает оси против часовой стрелки с 90-градусными инкрементами. ' АО не вращает оси
Вывод на терминал 259 вообще. AI поворачивает их на 90°, А2 - на 180° и АЗ - на 270°. Аналогично код ТА поворачивает оси на указанное число градусов от О до 360 (против часовой стрелки) и от О до -360 (по часовой стрелке). DRAW "AILI0" и DRAW "TA90Ll0" приведут к тому, что линия, которая должна была быть направленной влево, будет вместо этоrо нарисована повернутой на 90° и направлена вниз. Оператор DRA W может включать строковые переменные, которые состоят из набора допустимых кодов. Это свойство позво­ ляет программе повторно использовать части фиrур в различных рисунках. В операторе DRAW имя строки должно быть помещено за буквой Х и за ним должны следовать точка с запятой. Например: 100 S$ = "Ul2RISU4SL32" 110 DRAW "XS$;" В одном операторе DRAW может содержаться нескол~,ко строк, перемежаемых другими кодами. Отмстим, что любые числа, используемые с кодами в операторах DR АW, моrут сам и быть переменными. Таким образом, с помощью одноrо оператора DRA W могут выводиться фигуры, отличающиеся по форме, цвету, мас­ штабу и ориентации. Надо помес:rить знак равенства между буквенным кодом и именем переменной, а за именем поместит~, точку с запятой. Например, чтобы установить код цвета, опреде­ ляемый переменной, напишите DRAW "С = PCOLOR;". Компи,­ лятор Бейсика требует, чтобы ссылка на эти переменные осущест­ влялась с помощью функции VARPTR$. В этом случае такой опе­ ратор будет иметь вид DRAW "Х" + VARPTR$(S$) или DRAW "С=" + VARPTR$(PCOLOR). Сложные рисунки моrут быт1, сохранены в массиве и затем возвращены на экран в любой момент. Обсуждение этоrо вопроса см. в (4.4 .6 ]. ' Низкий уро11ень В приведенной ниже процедуре применяется алrоритм Брезен­ хэма для вывода прямой линии, соединяющей любые две точки. В ней используется функция BIOS вывода точек и ее можно убыстрить, если заменить эту функцию на встроенную проце.:1уру, использующую прямое отображение в память. Как и все быстрые алгоритмы, данная процедура избеrаст операций умножения и деления. Линия рассматривается как набор сеrментов двух типов: тех, которые расположены диагонально, и тех, которые распо­ ложены горизонтально или вертикально. Для линий с наклоном больше 1 прямые сегменты вертикальны, в противном случае они горизонтальны; первая задача алгоритма состоит в вычислении 9*
260 Глава 4 118JCJioнa. Затем вычисляется выравнивающий фактор, который следит, чтобы некоторое число прямых сегментов имело большую длину, чем остальные. И наконец, сложный цикл поочередно выводит диаrональные и прямые -сегменты. ВХ поочередно прини­ мает то· положительные, то отрицательные значения, отмечая, какой тип сегмента выводится. Ниже показано, как rотовятся данНЬiе для вывода диаrонали из одноrо угла экрана в противо­ положный: ;---в,сеrменте данных sт_х DW о END_X DW 319 ST_Y DW о END_Y DW 199 COLOR ЦВ 2 DI_Y_IN ·DW ? Dl_X _IN DW ? SH_D1 DW ? ST_X __IN DW ? ST_Y_IN DW ? sт_со DW ? DI_CO DW ? ;---установха режи~а дисплея MOV АН,О MOV AL,4 INT l0H ;функция установки режима ;цветной 320*200 ;установка режt1ма ;---установка начальных инкрементов ДJIЯ каждой 11озицt1и то•1кt1 MOV СХ,1 ;инкремент w111 oct1 х MOV DX,l ;инкремент w1я оси у ;---вычиспение вертикальной дистанции MOV DI,END_У ;вы•1итаем коорди11ату на•~алыюй SUB DI,ST~У ;точки из координаты ко11е•11юй JGE КЕЕР_У NEG DX NEG DI ;вперед, если 11акло11 < О, ;ина•1е,инкреме11т panc11 -1, ;а дистанция до,1ж1~а бмт1, > О KEEP_Y:MOV DI_Y_IN,DX ;---вычисление rоризонта:льной диста1щш1 MOV SI,END_X ;вмчtпаем коорщ,111ату 1щ•щл1,11оf1 SUB SI,ST_X JGE КЕЕР_Х ;точки ·из к~рди11аты ко11с•11юй ;вперед, если JIIIKJIOII < о.
Вывод на терминсд,, NEG СХ NEG SI КЕЕР_Х:МОV DI_Y _IN,CX • ;иначе.- инкремент равен -1, ;а дистаици11 должна быть > О 261 ;---определ11ем, rоризонтаяьны или вертикальны пр11мые с~rмеиты СМР Sl,D1 ;rорнзонтальные д,JU1н11ее? JGE HORS MOV СХ,О XCHGSI,DI JMP SAVS HORS: MOV DX,0 SAVS: моv SH_DI,DI MOV SТ_X_IN,CX MOV ST_Y_IN,DX ;---вычисп11ем выравнивающий фактор MOV AX,SH_DI SНL АХ,1 MOV ST_СО,АХ SUB AX,SI MOV ВХ,АХ SUB AX,SI MOV DI_CO,AX :---подготовка к выводу линии моv сх,sт_х MOV CX,ST_Y INC SI MOV AL,COWR ;---теперь выводим линию MLOOP: DEC SI JZ FINIS MOV АН,12, INT I0H СМР ВХ,О JGE DIUN, :---выводим пр11мые сеrменты ADD CX,ST_X_IN ADD DX,ST_Y_IN .лоо вх,sт_со JМР SHORT MWOP :---выводим ,!Рf8,:ональные сеrмеиты :если да, то вперед, :иначе - ДIIII ПJ)IIMЬIX х не МCHIICТCII :помещаем болыnее в СХ ;сохраи11ем значени11 ;теперь дn11 пр11мых не меи11етс11 у ;меньшее расето11ние ;один из них О, ;а·друrой - 1. . ;меньшее рассто11ние в АХ ;удваиваем ero :запоминаем ero ;2хме11ьшее - большее ;запоминаем как счетчик цикла ;2хменьшее - 2хбольurее ;запоми11аем 1 :начальная координата х ;начальна11 координата у ;прибамяем I дпи конца ;берем код цвета :счетчик дnи больwеrо- расстои11и11 :выход после последней точки :функции вывода точки ;выводим точку :если ВХ < О, то примой сеrмент :иначе - диаrональный сеrме11т :определ11ем инк,ре- • ;менты по ос11м ;фактор выравнивании ;на следующую точку
262 Глава 4 DILIN: ADD CX,DI_X_IN ;определяем ADD DX,DI_Y _IN ;ю1крсменты по осям ADD BX,DI_CO ;фактор пыра1111н11а11ш1 JMP SHORT Ml,OOP ;на следующую Н>•1ку FINIS: 4.4.6 . Запоnненне областей экрана Тщательное продумывание позволяет исключить излишнюю медлительность, свойственную многим программам заполнения областей для графического экрана. Когда заполнение основано на простых вычислениях, которые действуют по очереди для· каждой точки, требуются расходующие много времени битовые операции. Более экономный код может определять, все ли битовые позиции определенного байта видеобуфера должны иметь один и тот же цвет, и когда это условие выполняется, этому байту присваивается заранее заготовленное значение, которое устанавливает вес точки в правильный цвет. При этом нет необходимости повторят~, опера­ ции над одним и тем же байтом, каждый раз устанавливая биты только для одной из точек, информацию о которой содержит данный байт. В [4.3 .4] объяснено, как создать описание символа в виде матрицы 8*8 точек, имеющего требуемый вам вид. Хотя такие символы могут выводиться только в стандартные символьные позиции, их использование может существенно облсrчить запол­ нение графиков. Образец, высвечивающий все 8*8 точек можно вывести в интервале нсскол~,ких строк и столбцов, заполняя область намного быстрее, чем это достиrастся при поточечной зарисовке. Этот тип rрафичсских символов может применs1п,ся совместно с точечной rрафикой. Псевдоrрафи,1еские символы моrут служить также для вывода вращающихся или колеблющ11хсs1 объектов. Высокий уровень Бейсик предоставляет оператор PAINT для заполнения замк­ нутой фигуры произвольной формы. Вам необходимо указап, только точку внутри области, а об остальном позаботится проце­ дура. Может быть указан цвет палитры, которым надо заполнит~, область, например PAINT 000,110),2 заполняет облает~, цветом 2 палитры. Закраска ведется начиная от указанной точки до тех пор, пока не встретятся точки с цветом, отличающимся от фонового. Вы можете, наоборот, ук,iзать цвет границы, и закраска
Вывод ш1 терминал 263 будет продолжаться во всех направлениях, пока не будут встречены точки указанного цвета. При такой закраске линии других цветов, находящиеся внутри границы, могут быть также закрашены. Код цвета границы следует за кодом цвета заполнения, таким образом, PAINT (100,180) ,2,3 закрашивает область цветом 2 до линий цвета 3. Отметим, однако, что эта процедура не заполняет области, находящиеся "за углом", т.е. если вдоль какой-либо горизонтальной или вертикал1,ной траектории встретилась точка, имеющая цвет границы, все последующие точки вдоль этой траектории не заполняются, даже когда фигура имеет причудливую форму и эти точки принадлежат внутренней части фигуры. В следующем примере выводятся две перекрывающиеся рамки цветами циан и маrента, а затем последняя рамка заполняется б~лым цветом. Сегменты первой рамки, которые попадают в закрашенную область, также заполняются белым. ' 100 LINE (S0,70)-(270,130),1 ,B 110 LINE (100,30)-(220,170),2,В 120 PAINT (101,31),3,2 р~1суем рамку t~11етом циан р~1суем рамку ЦIIСП)М Mal'CIIПI заполш1ем 11торую рамку белым Помните, что команда LINE может сама заполнить рамку, (;.'СЛИ вы укажете в качестве параметра 'BF', а не ·в·, см. [4.4 .SJ. Оператор PAINT имеет "орнаментальные" возможности, кото­ рые позволяют заполнять области указанной картинкой. Элементы орнамента, которые в режиме умеренного разрешения имеют размер 4 точки в ширину и· 8 в высоту (8*8 для высокого разре­ шения), повторяются по всей указанной области. Рисунок описы­ вается набором байтов, содержащих цепочку битов для последова­ тельных рядов элемента орнамента. В режиме умеренного разре­ шения цепочка битов 10000011 описывает 4 точки, первая из которых имеет цвет 2, следующие 2 - фоновый цвет, а последю1я - цвет 3. Эта цепочка соответ~твvет числv 131 или &Н83 (см . .п риложение Б, где обсуждаются· битовые· операции в Бейсике). Обращение этой цепочки в 11000010 даст 193 (&HCI). Они могут быть объединены в элемент орнамента шириной в 4 точки и высотой в 2 точки строкой CHR$(&H83) + CHR$<&HC1>. В такую строку можно включать до 8 байт, доводя высоту до 8 точек. Такая строка испол1,зуется • в операторе· PAINT вместо цвета. Вот вывод квадрата, заполненного описанным орнаментом: 100 LINE (100,ll0HIS0 ,150),1,B 'рисуем рамку 110 PAINT (12S,12S),CHR$(&H83) + CHR$(&1ICI ),1 ·за110;111wсм се
264 Глава 4 Отметим, что нерегулярности элемента орнамента могут приводить к тому, что процоура PAINT завершаетсs1, не закончив заполнения области. Бейсик решает эту проблему указанием пара­ метра фона для оператора PAINT. Если у вас возникнfг проб­ лемы, обращайтесь к рукове,дству по Бейсику за деталями. Оператор DRAW, поз1-<оляющий рисовать сложные линии, также может заполнять области. Он обсуждается в [4.4 .5 ]. "Текущая· точка" (из которой будет рисоваться следующий сегмент линии) должна быть помещена внутрь области, ограни­ ченной линией указаннш·о цвета. В строку оператора DRA W надо поместить кодовую букву Р, за которой должек следовать код цвета закраски и код цвета границы. Для вывода рамки цветом 1 палитры, а затем для ее заполнения цветом 3 напишите DRA W "Ul0RI0DI0Ll0BHlPЗ,l ". Здесь первые четыре кода рисуют границы рамки, затем код 'ВН' перемещает текущую точку внутрь рамки, не рисуя линии, а затем код ·р· приводит к заполнению рамки. Таким образом могут быть заполнены и более сложные формы. Отметим, что необязате.11ьно при перемещении точки внутрь области отменять рисование линии вдоль ::>того пути. Однако в данном случае надо испол~,зовать для этого сеrмент'а код цвета, отличный от цвета заполняемой границы. Бейсик имеет также возможность заполнения областей ::>крана заранее подготовленным изображением. Изображение может быть любого размера, может быть выведено в любой поз~щии ::>крана и храниться в массиве. Обычно изображение создается с помощью всех доступных средств, а затем запоминается в массиве опера­ тором GET. Массив может быть помещен в последовательный файл (5.4 .3 ], из которого программа может загрузить его и вывести изображение. ОпеР.атор GE_T п~речисляст координаты левого верхнего и правого нижнего угла рамки, содержащей изображение, причем сначала идет номер столбца, а затем номер строки для каждой пары координат. Затем должно следовать имя массива, которое не заключается в кавычки. Например, G ЕТ (80,40)-(120,60),ARRAYЗ помещает все точки, находящиеся внутри указанной области, в массив с именем ARRA УЗ. Одномерные массивы, как и все остальные, должны ·бып, предварительно описаны оператором DIM. Массив может содер­ жать элементы любой точности. Для вычисления требуемых размеров массива надо сначала определить, сколько байтов потре­ буется для хранения изображения. Это можно вычислить по формуле 4 + INT ((Ххбитовнаточку + 7) /8)х У. Здесь "битов­ наточку" = 1 для высокого разрешения и 2 - для умеренного. Буквы Х и У относятся к числу точек вдоль горизонтальной и вертикальной сторон блока изображения. INT обозначает целую часть числа. Наконец, надо определить, сколько элементов
Вывод иа термuиал 265 ма~сива требуется для хранения. данного числа байта"в.· Каждый элемент занимает 2 байта в целом массиве, но 4 ::-:1-я чисел с обычной точностью ~ 8 - для чисел с двойной точностью. Для получен11я изображения из массива и вывода его на :жран служит оператор PUT. Этот оператор требует тол1,ко координаты левого верхнего угла области экрана, в которую будет выводиться изображение. За координатами должно б1:,1ть указано имя массива. Например, PUT (40,30) ,ARRAУ 1 помещает изображение, левый верхний угол которого будет находиться в столбце 40 и строке 30. Оператор PUT может иметь еще и необязательный параметр, определяющий цвет, которым будет выводиться изображение. Если этот параметр опущен, то изображение будет выводиться точно в том виде, в котором оно было записано оператором GET. Это эквивалентно записи PUT (40,30) ,ARRAYl ,PSET. В противном случае имеются некоторые другие возможности. Если в1:,1 вместо PSET укажете PRESET, то цвет О палитры буд~т заменqн на цвет 3 и наоборот, а цвет 1 палитры - на цвет 2 и наоборот. Существуют еще три ситуации с логическими операциями AND, OR или XOR. Как и PRESET, эти слова могут заменят~, PSET в приведенном примере. Обсуждение этих трех операций содержится в приложении Б. Каждая операция включает срав­ нение битов существующей точки на ::~кране с битами точки накладываемого изображения. В режиме высокого _разрешения, когда на точку отводится только l бит, операция простая. Но в режиме умеренного разрешения, в котором на каждую точку отво­ дится по 2 бита, могут происходить различные .трансформации цветов. AND устанавливает бит, только если- он был установлен и у точки экрана, и у точки изображения (взятой из массива>. В режиме высокого разрешения это озн.t•1ает, что точка изображения появится на экране, только если соответствующая точка ::>крана уже "включена". Все остальные точки области будут выключены. В режиме умеренного разрешени,~ операция производится над обоими qитами. Если для точки :экрана установка битов О 1, а для соответствующей точки изображения - l О, то оба бита будут сбро­ шены и точка экрана получит код 00, что соответствует фоновому цветv. OR устанавливает би~, если он был установлен либо для точки экрана, либо для точки изображения. В черно-белом режиме OR наклад~вает изображение на· существующее изображение на экране. В цветном режиме для определения эффекта вы снова должны прибегнуть к вычислениям. Комбинация кодов палитры 1(01) и 2(10) дает 3(11), так же-как и комбинация 0(00) и 301). И наконец, XOR устанавливает бит, если из двух •сравни­ ваемых только один был установлен. Применение этой операции
266 Глава 4 для черно.-белого экрана с массивом единиц обеспечивает негатив­ ное изображение О и l даст О, а l и О - l). В режиме умеренного разрешения эта операция меняет все цвета. В резут,тате получаем наложение двух изображений. Но более важ,ю, что при повто­ рении этой операции экран принимает в точности такой же вид, который он имел первоначально. При этом изображение стирается. Такая методика полезна для мультипликации, когда над изобра­ жением дважды производится операция XOR в одной позиции, затем в соседней и т.д. Низкий уровень Существует много подходов к написанию процедур заполнения графических объектов. Ни один из них не является идеальным, поскольку всегда возможен конфликт между скоростью работы процедуры: и сложностью фигур, которые она может обрабатывать. Любая процедура, заполняющая область точка за точкой, будет медленной независимо от того, насколько элегантно она реали­ зована. Имейте в виду, что почти каждая модифицируемая точка расположена в байте, все точки которого будут изменят~,ся в тот же цвет. Получение доступа к одному и тому же байту с испол1,­ зованием сложных процедур требует существенно бол1,ше времени, чем установка целого байта за один доступ к ячейке видеобуфсра. Например, поточечная очистка :>крана требует на IBM РС нескольких секунд при применении функции BIOS, в то время как прямой доступ в память производит эту операцию мгновенно: MOV АХ,ОВ800Н MOV ES,AX MOV СХ,8192 MOV АХ,О MOV DI,0 REP STOSW ;ES указь111ает 1ia буфер :,крана ;заполняем все байты ;в каждый байт ш1шем О ;DI поочсред1ю указывает на все байты ;повторяем запись 8192 pa:ia Многие процедуры заполняют по одной горизонтальной строке, проверяя на цвет границы справа и слева. Поскольку строки сос­ тоят из смежных байтов данных, надо поочередно брат,,· байты из видеобуфера и проверять, присутствует ли в них цвет границы. Если цвет границы отсутствует, то можно заменить сразу весь байт на цвет заполнения. В противном случае к данному байту применяется поточечный подход. Есть очень быстрый способ определения, присутствует ли граничный цвет в данном байте видеобуфера. Предположим, что процедура ищет цвет 1 палитры в режиме умеренного разрешения с четырьмя цветами. Этому цвету соответствует код О 1, поэтому
Вывод па термипал 267 сначала заполним весь байт этим кодом: 01010101. Затем восполь­ зуемся операцией NOT для обращения каждого бита, после чего байт примет вид 10101010. Проделаем операцию XOR со значе­ нием, взятым' из видеобуфера; в результате получим байт, у кото­ роrо оба бита, относящиеся к· одной точке, равны I ТОЛl,КО для точек, имеющих гра ,ичный цвет. За,:ем снова применим операцию NOT с тем, чтобы пара битов, относящихся к точке rраничн·ого цвета, имела код 00. После этого используем операцию TEST для нахождения полей со значением 00. Если такое поле найдено,· то граничный цвет обнаружен и процедура переходит к обычному поточечному анализу данного байта. Эту процедуру можно еще ускорить·, если использовать славные данные. MOV AL,ES: [ВХ] XOR AL,101010108 NOT AL TEST AL, 110000008 JZ FOUND_IIOUND TEST Al,,001100008 JZ FOUND_8OUND TEST Лl,,000011008 JZ FOUND_IIOUND TEST AL,00000011В JZ FOUND_BOUND MOV AL,FILL_COLOR MOV ES: [ВХ] ,AL FOUND_BOUND: ;берем баrп ••з 11щ1собуфсра ;уета11авлш1асм биты д.пя ц11ста 1·рашщI,I ;обращаем б~п1,1 ;r1ро1~еряем биты 7 ;11ереход, се.пи rраш1 1 111ый 1111ст ;щхщсряем биты 5 ' ;переход, се;ш I·ра11и 1 111ый щ,ст ;11ро11срясм биты З ;IICJ)C~OД, CCJl5-I 1·ра1н-1 1 111ый ltllCT ;11ро11еряем биты 1 ;переход, ее.пи 1·р;11ш 1 1111,1й нI1ст ;rрани•шоrо ц11ета нет, за~ю,II111см байт ;возвращаем байт 11 1ш11собуфер Когда это возможно, постарайтесь, чтобы границы прямо­ угол~ных областей ваших картинок были выравнены на границу двух, четырех или восьми точек с тем, чтобы прямое отображение в память имело дело с целыми байтами. Другая возможность, хотя и при меньшей скорости, состоит в создании определяемых пользо­ вателем псевдографических символов [4.3 .4] и выводе их на границе области заполнения. Короче, в данной области вы всегда можете проявить сообразительность, а зачастую стоит подумать, нужна ли вам столь сложная графика в данной ~~аче. 4.4.7 . Графический вь1вод с использованием символов псевдоrрафики Когда вы выводите изображение точка за точкой, это отнимает очень много време~и, особенно когда создаются эффекты муль'Ги-
268 Глава 4 пликации. Один из способов экономии времени состоит в сведении всех или части выводимых форм к фигурам, которые могут быть построены на матрице точек 8*8. Такие фигуры могут быть созданы как определяемые пользователем символы, см. [4.3 .4 ]. После тоrо как эти символы определены, они выводятся на экран очень быстро и просто. Эти символы могут выводиться вперемешку с поточечными графиками, как обычные буквы. Один из способов быстрого заполнения фигуры состоkт в последовательном выводе внутри фигуры полностью закрашенного блока. Отметим, что эти символы всегда располагаются в стандартных позициях курсора. Средний уровень В этом примере рисуется фигура человека, занимающа~ 2 символа в высоту и 2 символа в ширину. Как объяснено в [4.3 .4 ], вектор прерывания lFH указывает на начало области данных, определяющих символы. Четыре символа могут быть выведены обычными процедурами DOS или BIOS. Легко создать набор символов для вывода фигуры с руками и ногами в другом месте экрана. Два набора символов могут поочередно меняться в сосед­ них позициях курсора, создава.я иллюзию идущего по экрану человека. ;---в сегменте данных CНAR_DATDB 00110000В ;левый верхний кuадра11т DB 01100111В D8 Ol 1001118 D8 001100118 D8 000111118 D8 00001111В DB 000011118 DB 00000111В DB OOOOOOIIB ;правый верхний кuадрант DB 100011008 D8 100110008 D8 001100008 D8 ll 1000008 D8 l 10000008 D8 110000008 D8 100000008 D8 000011118 ;левыr1 ш1жний кщщр:шт DB 00011 l 11B
Вывод на тер.минш,. D8 000111008 D8 000110008 D8 00011000В D8 001100008 DB! 011000008 D8 000100008 DB 11000000В D8 11000000В DB 110000008 1;>8 110000008 D8 011000008 D8 011000008 D8 000100008 D8 000111-108 D8 00000000В ;---установка вектора прерывания ;праВЬIЙ IШЖIIИЙ квадра11т PUSH DS 1 ;сохр.1няем DS MOV DX,OFFSET CHAR_DAT ;смещеiше д;111 да1111ых u ОХ. MOV AX,SEG Ci-lAR_DAT ;сеrме11т ДJ1я дан11ых 11 DS MOV DS,AX MOV АН,25Н MOV AL,IFH INT 21Н РОР DS ;---рисуем фигуру ;функция устанш1ки 1~сктора ;номер вектора ;устанаw1ш1аем 11ектор ;uосст:111аw1•шасм [)S ;---позиционируем курсор на верхний ряд MOV АН,2 ;функция уст1111,111к•1 курсора 1-;ov DH,13 ;строк~, 13 MOV DL,20 ;столбец 20 MOV 81-1,0 INT l0H ;стршrи,щ О ;уста1ю11ка курсора ;---рисуем верхние д,sа симuолn MOV DL,128 ;берем сим1юJ1 1211 MOV АН,2 ;функция 11ы11ощ1/курсор 1111срсн INT 21Н MOV DL,129 INT 21Н ;вывод СИМUОШI ;берем символ 129 ;выводим ею ;---позиционируем курсор на нижнюю строку MOV DH,14 ;строка 14 269 ·"
270 MOV DL,20 MOV АН,2 INT !ОН ;столбец 20 ;функция установки курсора ;устанавливаем курсор ;---рисуем нижние два символа MOV DL,130 ;берем символ 130 MOV АН,2 INT 21Н MOV DL,131 INT 21Н ;функция вывода/курсор вперед ;вывод символа ;берем символ 131 ;выводим его Глава 4 4.5 . Сдвнг экрана н страннцы Сдвиг экрана и разбиение на страницы - это два способа переноса блока информации из памяти на экран. При сдвиге одна из границ экрана сдвигается внутрь, стирая инфор­ мацию на противоположной стороне. Затем освободившаяся область заполняется из памяти. Повторение этого дсйствю1 строка за строкой создает иллюзию сдвига экрана. С другой стороны, разбиение на страницы основано на одно­ временном хранении нескольких экранов информации в видео­ буфере и переключении вывода с одной страницы на другую. Использование дисплейных страниц невозможно на монохромном адаптере, поскольку его памяти хватает только для одного символьного экрана. Другие видеосистемы в большинстве экран­ ных режимов могут работать с нескол~,кими страницами. Исполь­ зование страниц дисплея особенно полезно при построении сложных картин "за кулисами"; после того как эта работа завер­ шена, новый экран выводится моментально. Процедура, имитиру­ ющая работу со страницами для монохромного адаптера, приве­ дена в [4.5.З ]. Она особенно полезна, когда вы имеете дело с медленным выводом на экран в Бейсике. 4.S .1 . Вертнкальный сдвиr текстовоrо экрана Когда текстовый экран сдвигается вверх, строки со 2-й по 25-ю переписываются на строки с 1-й по 24-ю, а следующая строка данных выводится в 25-й строке. При этом верхняя стрька, поверх которой осуществляется вывод, теряется, хотя она продолжает существовать в памяти. Сдвиг вниз устроен аналогично.
Вы.вод на терминал 271 Высокий уровень Бейсик утомительно медлителен при манипуляЦlfЯХ с экраном. Для быстрого сдвига вы можете воспользоват1,ся процедурой на машинном языке, которая не делает ничего другого, кроме преры­ вания IOH, см."Средний уровень". Процедура позволяет сдвигать весь -экран или любое окно в нем. В приложении Г показано, как включать· подпрограммы на машинном языке· в программы. Ваша программа на Бейсике должна указывать коорд"инаты верхнегd левого и нижнего правого углов окна, которые могут лежать в диапазоне от О до 24 и от О до 79. Требуется также параметр, ука­ зывающий направление сдвига: вверх или вниз (6 и 7 соответ­ ственно), число строк, на которое нужно сдвинуть (если О, то окно очищается), и .значение байта атрибутов для очищаемых строк (для "нормальных" - 7). Используйте для· них целые переменные. В приведенном примере экран сдвигается вниз на одну строку, а затем освободившаяся строка заполняется. • 100 "'данные для подпрограммы 110.DATA &Н55, &Н88, &НЕС,_&Н88, &Н76, &Н12, &Н8Л 120 DATA &Н24, &Н8В, &Н76, &НIО, &Н8А, &1-104, &J-188 130 DATA &Н76, &НОЕ, &Н8А, &Н2С, &1-188 , &1-176, &НОС 140 DATA &Н8А, &НОС, &Н88, &1-176, &J-IOЛ, &J-18Л, &J-134 150 DATA &Н8В, &1-176, &НО8, &Н8Л, &1-114, &1188 , &1176 \ 160 DATA &НО6, &Н8А, &НЗС, &HCD, &1-110, &H5D, &1-IСЛ • 170 DATA &НОЕ, &НОО 180 '"помещаем данные в сегмент &1-12000 190 DEF SEG = &Н2000 ~ ·помещаем щ111111,1с 1нi 1 11111as1 с &112000 200FORN=ОТО43 '210 READ Q 220 РОКЕ N,Q 230 NEXT 300 '"в программе ·4 4 байта ·•11паем одш1 байт 'помеuщем Cl'O в памяп, ·следую1щ1й 310 GOSUB 500 'сд1шгаем на строку 320. LOCATE 1,1: PRINT ТЕХТ$(LINР);'выоод11м строку текст.1 500 '"подпрограмма сдв11га 510 DEFINT A,Z 520Тl,R=0 530 TLC О 540 BRR 24 550ВRС=79 flCIIOJl/,Зyeм целые IICJJCMCIIIIЫC левая 1~срх11я11 строка ·лс111,1й 11ерхш1й столбе,~ '11ижш111 щmвая строка ШIЖIШЙ 11ра1!1,IЙ CTOJIOCH ·
272 560 NUМROWS 570DIR=7 580FJU, =7 590 DEF SEG = &Н2000 600 SCROU, = О Глава 4 •число строк сдвига •направление сдвига вниз 'заполнение обычным атрибутом ·указываем на подпрограмму ·начинаем с 1-ro байта 610 CALL SCROU,(DIR,NUМROWS,TLR,TLC,BRR,BRC,FILL) 620 RETURN Средний уровень 'все сделано Функция 6 прерывания 1ОН сдвигает любую часть экрана вверх, а функция 7 - вниз. В обоих случаях AL содержит число строк сдвига, а когда AL " О,· весь экран чистится, а не сдви­ гается. CH:CL содержат строку и столбец левого верхнего угла, а DH:DL - координаты правого нижнего угла. Появляющиеся из-за сдвига строки - чистые; они выводятсst с кодом атрибутов из ВН. ,---сдвиг вверх на одну строку MQV АН,6 ;номер функции с~виrа вверх MOV AL,l MOV сн,о моv CL,0 моv DH,24 MOV DL,79 MOV \ ВН,7 INT I0H Низкий уровень ;число строк сдвига ;строка левого верхнего угла ;столбец левого верхнего угла ;строка правого нижнего угла ;столбец правого нижнего угла ;атрибуты очищаемой строки ;делаем сдвиг Вертикальный сдвиг всего экрана - тривиальная задача, поскольку правая граница однои строки в памяти продолжается левой границей следующей строки. Сдвиг всего содержимого видеобуфера на 160 байт вверх по памяти (80 символов в с:rроке х 2 байта на символ) приводит к сдвигу экрана вниз на одну строку. Если вы пишете· свою собственную процедуру сдвига экрана, использующую прямое отображение в память, то не забывайте об интерференции, которая возникает на цветном диспдее и PCjr. Эта проблема обсуждается в (4.3.1 ]. Решение этой проблемы - про~е­ рять статусный байт, ожидая, пока он разрешит запись в видео­ буфер. Вам придется поэкспериментировать, чтобы определить, сколько данных вы можете записать за один цикл.
\ Вывод на терминал 273 Другое решение состоит в выключении экрана на время оnер.t­ ции сдвига, а затем в его восстановлении. "Выключение экран.t" подразумевает, что вывод содержащихся в, видсобуфсрс данных запрещен, но сам буфер не изменяется. Описанный процесс поль­ зуется функцией сдвига BIOS. Это нс очсн1, приятно для глаз, но все-таки не так плохо, как уже упоминавшаяся интерференция. Для выключен.ия экрана у цветного графического дисплея надо сбросить бит З порта с адресом 3D8H. Изменi;ние бита назад на 1 моментально вновь включает экран. Адрес порта соответствует регистру выбора режима цветного графического адаптера. Этот однобайтовый регистр только для записи; по:rгому программа не может просто прочитать его, изменить знач~ние бита З и всрну1'ь прочитанный байт. Вместо этого необходим~;, определить также правильную установку всех остальных битов (перечисленных в [4,1.2 ]). Для PCjr этот бит расположен в реr'истрс управления режимом l массива ворот дисплея. В [4.1.1 ] объяснено, как получить доступ и запрограммировать :)ТОТ регистр. 4.S .1. Сдвиг текстового экрана горизонтально Горизонтальный сдвиг иногда требуется в специальных прог­ раммах обработки текста, таких, как текстовые редакторы. Опера­ ционная система не имеет для этоrо специальных средств. По этой причине данная задача немного сложнее, чем вертикальный сдвиг. Рассмотрим ситуацию, когда вы хотите, чтобы экран сдвигался влево на 5 позиций. При этом левые 5 столбцов исчезнут, весь остальной текст сдвинется влево, а самые правые 5 столбцов будут очищеt1ы. Видеобуфер представляет собой одну длинную строку. Поэтому если каждый символ буфера сдвинуть на 10 байт вниз, то суммарный эффект будет состоять в том, что самые левые 5 сим­ волов каждой строки будут передвинуты 1'1 последние 5 позиций предыдущей строки. Таким образом, весь :жран будет сдвинут влево на 5 позиций, передвигая 5 ненужных столбцов в пр.tвую часть экрана. Все, что после этого остается, - это очистить правые 5 столбцов. Это легко делается с помощью процедуры вер:rи­ кального сдвига [4.5 .1 ), которая может выполняться для любой части экрана и которая очищает указанную область, если указать сдвиг на О строк. Рис. 4.6 иллюстрирует описанный метод. • Низкий уровень В этом примере осуществляется сдвиг на 5 позиций влсRо. • Легко изменить его для сдвига вправо или для другого значения
274 Глава 4 позиций сдвига. При использовании прямого отображения память этот метод дает практически момснтат,ный сдвиг :>крана. rНаложение . Стираем этот столбец, l испольэуfl фунцию вертикального сдвига г-;------------------- ~: ,. ,. •; '·. '· ,. ,. ,. ,: '. '. ,. 1: ,. ,. :-4---- - --- ---- ---- ----~- Столбец 1 Столбец ВО Столбец 1 Рис .. 4 .6. Гор~1зонтал1,11ый сдвнI· :жра11а ;---сд11иrаем все Rниз на I О байт MOV АХ.080001-1 ;указы11асм Iia буфер 11,10IюхромIюю MOV ES.AX ;днс11лся MOV DS,AX MOV SI,10 MOV D1,0 MOV СХ,1995 REP MOVSW ;---оч11щаем правый край MOV АН,6 MOV AL,0 MOV СН,О MOV CL,75 MOV DH,24 MOV DL,79 MOV ВН,7 INT IOH ;с11I1ш11см н~ SI ... ;... 11 DI ;сдвигаем все. кроме 1юслсJ1шIх 5 байт ;осущССПIJIЯСМ сд11ш· ;функция нертнкат,,юГ(, сд11ига ;сдnнг на О строк •I~Iспп окно ;строка лс11оп, 11срхнсГ() уI·ла ;сто;~бс11. лсво1·0 всрх11с1'<) у1·:1а ;строка 11раво1·0 11иж11с1х, у1·;ш ;столбец 11раво1х> 1н-1ж11с.::1У) у1·ла ;атрибут ;uI~ оч1-111tасмых IIозн1t1-1й ;чистим окно Столбец ВО
\ Вывод на терминал , 27'5 ·• 4.5 .3 . Перекnючение между текстовыми страницами Поскольку все видеосистемы, кроме монохром\iоrо дисплея, имеют достаточно памяти для нескольких видеобуферов, одновре­ менно могут быть с.,онструированы несколько экранов, каждый из которых может быть выведен в нужный момент. Вместо того чтобы передвигать данные в видеопамяти, монитор посылает данные из другой области видеопамяти.' Число доступных страниц может меняться в зависимости от видеосистемы и режима дисплея. Приводим кра·ткую сводку: Режим Тип Число с1:nаш111 1lачало б\'illc12a о алфавип10-цифро1юй 8 111100 1 алфавипю-ц~1фро1юй 8 BII00 2 алфавитно-цифровой 11 111100 3 алфавитно-цифровой 8 шюо 4 графи•1еский BII00 5 графический В8(Ю 6 графический 1 BII00 7 алфавитно-цифровой 1/8 111100 8 графический 11ереме111юе IIIIOO 9 графический переме1111ос HII00 А графический перемс1111ое В800 D графический 2/4/8 лооо Е графический 1/2/4 лооо F графический 1/2 лооо 10 графический 1/2 лооо Режимы 8-А - графические режимы PCjr; число страниц для них меняется в зависимости от того, сколько оперативной памяти отведено под видеобуфер. Размер страницы равен 2К или 4К для алфавитно-цифровых режимов, 32К - для четырех цветов при высоком разрешении или 16 цветов при умереffном разрешении и 16К - для всех остальных режимов. Режимы D-10 поддерживаются EGA. Количество страниц меняется в зависимости от устаноR­ ленной памяти. Режимы F и 10 требуют наличия не менее 128К памяти. Режим 7 разрешает одну страницу для монохромного адаптера и 8 страниц для EGA. Монохромный адаптер не имеет памяти для дополнительных страниц. Однако нет никаких nри•1ин, по котоrым часть основной памяти нельзя было бы использовать как буфер дисплея. В этом случае страничная организация осуществляется за сч~т быстрого обмена всего содержимого буфера в памяти с видеобуфером (адрес которого ВООО:0000). Буфер в основной пам~и можно рассмат-
276 Глава 4 ривать как "псевдостраницу". Хотя это и не настоящее разбиение на страницы, результат будет почти такой же, если для перес·ылки данных вы будете использовать ассемблерную процедуру. При использовании страниц надо позаботиться о том, чтобы операции вывода на экран направлялись на нужную страницу. Программа не обязана выводить данные на ту страницу, которая в данный момент изображается на экране. На самом деле часто желательно конструировать экран "за кулисами", а затем момен­ тально выводить готовое изображение. Этот иетод особенно поле­ зен, когда необходимо конструировать сложный вывод в Бейсике, у которого вывод очень медленный. BIOS хранит в своей области данных однобайтовую переменную, указывающую, какая из страниц выводится в данный момент. Диапазон значений этой переменной от О до 7. Она расположена по адресу 0040:0062. Высокий уровень В Бейсике предусмотрена команда SCREEN для установки страницы, на кQТорую будет идти вывод (активной страницы>, и выводимой страницы (видимой страницы). Страницы нумеруются отОдо3длятекстовс80символамивстрокеиотОдо7для 40-символьных. Третий параметр за командой SCREEN устанав­ ливает активную страницу. SCREEN"2 приводит к тому, что все операторы PRINT будут работать со страницей 2. Четвертый пара­ метр устанавливает видимую страницу. SCREEN,,,l приводит к тому, что на экран будет выводиться страница 1. Если указать . SCREEN"2, 1 - то вывод будет направлен на 2 страницу, а види­ мой будет страница 1. Когда видимая страница не указывается, автоматически принимается, что она совпадает с активной. Для выделения памяти под страницы на PCjr используется оператор CLEAR. Этот оператор устанавливает общее количество памяти, отводимое под буфер экрана, которое при старте равно 16384 байтам. Чтобы добавить вторую страницу размером 16К, напишите CLEAR,,,32768. Добавочные текстовые страницы требуют 4096 байт каждая. При условии, что так.ttм способом была отведена память, команды оператора SCREEN для работы со стра­ ницами будут действовать ранее описанным образом. Только PCjr имеет добавочный параметр оператора SCREEN, который стирает страницу (т.е. переводит ее в цвет фона). Детали описаны в руко­ водстве по Бейсику. Оператор РСОРУ также уникален для PCjr. Он копирует изображение из одной страницы в другую. Например, РСОРУ 2, 1 целиком копирует страницу 2 на страницу l. Хотя монохромный адаптер не имеет памяти для страниц дисплея, существует способ- устроить своего рода "псевдо­ страницы". Приведенная ниже процедура на машинном языке рас- (
Вывод на терминал 277 сматривает блок памяти ка~ дисплейную страницу. При вызове этой процедуры она обменивает содержимое видеобуфера с содер­ жимым этой области памяти. В результате мы имеем как бы две дисплейные страницы. (В приложении Г объясняется, как вклю­ чать подпрограммы на машинном языке в программы на Бейсике.) Вы должны отвести блок памяти размером 4000 байт для псевдостраницы, помимо памяти, содержащей программу на машинном языке. В примере блок начинается с адреса сегмента &Н2000, а процедура помещена по адресу &Н2200. Сегментный адрес блока содержится в 9-м и l 0-м байтах машинного кода, и вы легко можете изменить его. Видн:о, что адрес &Н2000 представлен как &НОО, &Н20 в операторе DATA. Это следствие того, что младшие цифры всегда размещаются в младших ячейках памяти. Если вы хотите разместить блок, скажем по адресу 1234:0000, то надо изменить байты 9 и 10 на &Н34, &Н12. Вам может потребоваться очистить псевдостраницу от всякой ерунды, оставшейся от других. прогр_амм. В строках 230-260 это достигается за счет засылки символа пробела (ASCII 32) в каждый байт (32 служит "нормальным" байтом атрибутов). Программа может осуществлять вывод на экран обычным образом, а затем переносить содержимое на псевдостраницу. Но при желании вы можете осуществлять вывод непосредственно на пссвдостраницу, используя прямое отображение в память. 100 ""машинный код 110 DATA &НIЕ, &НО6, &нns; &НОО, &НВО, &Н8Е, &НСО 120 DATA &HD8, (3&НОО, &Н20), &Н8Е, &HD8, &1-ШF, &1100 130 DATA &1100, &НВЕ, &Н:00, &НОО, &\iFC, ~НВ9, &HD0 140 DATA &1!07, &1126, &Н8В, &HlD , &HAD . &НАВ, &1-189 150 DATA &HSD, &lll'E , &НЕ2, &HF6 , &Н:07, &ШF, &НСВ 160 """помещаем код 11 11амять 170 DEF SEG = &Н2200 180FORN=ОТО34 190 READ Q 200 РОКЕ N,Q 210 NEXT 220 ···чистим псведостраницу 230 DEF SEG = &Н2000 240FORN=ОТО3999 250 РОКЕ N,32 260 NEXT у~mзываем адрес процедуры ·начинаем с первого байта ·читаем байт процедуры ·пишем его в память 'адрес начала щ:евдостраницы ·для каждого символа и атрибута ·помещаем код 32 ·пока не очистим весь буфер
I 278 Глава 4 500 '"пишем прямо в 11се1щострашщу укnзыпнсм на ее адрес 510 DEF SEG = &1-12000 520 S$ = "PSEUDOPAGE" 530 М = LEN(S$) в1,шодим слово посреди стран~щы получаем длш1у строк~, 540FORN=1ТОМ 'для каждого сим,юла строк~, 550 РОКЕ N*2 + 2000, ASC(MID$(S$,N,1 )) ·помещаем CI'() в буфер 560 NEXT 600 "'теперь используем процедуру 610 PRINT "SCREEN 1" ·пе•шп1ем сообщеш,е на :жрав 620 DEF SEG = &1-12200 указ1,1щ1ем на 11роцедуру 630 PSEUDOPAGE = О начшшем с 11а•ш;ш щхщедуры 640 CЛlJ, PSEUDOPЛGE 'обмсшшасм страшщы 650 CALL PSEUDOPAGE ·повторяем обме11 660 ,,, Средний уровень .:>у1-1кц11я 5 прерывания I ОН выбнрас,· текущую страницу дисплея для вывода. Надо просто помесп1т1, номер страницы в AL: ;---уста~ювка видимой страшщ1,1 MOV ЛН,5 MOV ЛL,2 INT 101-1 ;номер фу11к11ш1 ;номер страшщ1,1 (шttш1ш>1 с 0) ;уста11а11ли11аем стрщl~щу· Однако эта функция не устанавливает страницу, на которую будет идти вывод. Любое из прерываний BIOS, которые выводs1т на экран (функции прерывания IOH), требует, чтобы номар страницы был указан в качестве входного параметра в одном из регистров. Однако все прерывания вывода на экран MS-DOS пишут на теку­ щую видимую страницу. Таким образом, для "закулисных" опера­ ций необходимо пользоваться прерыванием IOH. Для получения информации о текущей странице надо выпол­ нить функцию F прерывания \ОН, которая возвращает статус дисплея. Номер страницы при этом возвращается в ВН. Низкий уровень Дисплейные страницы выбираются за счет изменения точки видеопамяти,. начиная с которой монитор принимает данные. Эта
Вывод на терминал 279 точка памяти устанавливается региtтрами 12 (старший байт) и 13 (младший байт) микросхемы 6845, которые называются регис­ трами стартового адреса. Значения адресов раздела страниц для буфера, начинающегося с В800, такие: СтJ!аница 40 C•tMВOЛOIJ IIO CИMIIOJIOII о оооон 00001·/ l 0400Н 0800/-1 2 0800/-1 /ОООН 3 осоон 1800Н 4 lOOOH 5 1400Н 6 1800Н 7 lCOOH В [4.l .l] объясняется, как программировать регистры микро­ схемы 6845, а в [4.5 .4] содержится пример программирования стартового адреса. В последнем примере надо просто присвОJ!ТI, ВХ одно из значений приведенной таблицы. Конечно, при этом уста­ навливается только выводимая, страница. Для записи в опреде­ ленную страницу на низком уровне надо использовать одно из значений таблицы в качестве смещения в видеобуфере при прямом отображении в память. Поскольку прямое отображение в память работает очень быстро, иJiлюзия страниц может быть легко создана на моно­ хромном дисплее. Выделите блок размером 4000 байт для хране­ ния страницы. -Хотя монохромный адаптер не может непосред­ ственно читать из обычной памяти, содержимое этого буфера и видеобуфера, можно обменять насrол~,ко быстрсi, что никто нс заметит разницы. Следующая проце,дура обменивает содержимое этих двух областей. ;---в сегменте данных PPAGEDW 2000 DUP(720Н) ;заnол11яем буфер пробелам•~ ;---пересылка между nсе8Достран•щей •1 видеобуфером MOV АХ,О~ОООН ;указыв.,ем на шщеобуфер MOV ES,AX MOV AX,SEG PPAGE ;указываем 1ш nсеuдостра11ицу моv DS,AX REPE: MOV Dl,0 ;DI на начало nидеобуфера MOV SI,OFFSET PPAGE ;SI ·на на•шло nсеодостраi1ю11,1
280 Глава 4 CLD ;направление вперед MOV СХ,2000 NEXT: MOV BX,ES: (DI] LODSW ;-будем пересылать 2000 сло11 ;берем слово из в~щеобуфера в ВХ ;слово из псевдостраницы п АХ ;слово из АХ в видеобуфер sтosw MOV DS:[DIJ-2 ,BX LOOP NEXT ;слово из ВХ в пtевдострашщу PCjr хранит регистр страницы в порте с адресом ЗDFH .. Значение битов этого регистра следующее: биты 2-0 какая страница выводится (от О до 7) 5-3 . какая страшща ш1шется (от О до 7) при выподе по адресу сеrме1па В800Н 7-6 00 д.11я всех текстовых режимов = 01 д.11я графических режимов с 16К = ) 1 д.11я графических реж~1мов с 32К 4.S .4. Сдвнr между страннцамн текста Поскольку страницы текста прилегают друг к другу в видео­ буфере, небольшой текстовый массив может целиком помещап,ся в этой памяти. В этом случае текст может сдвигаться вверх и вниз по экрану, не передвигаясь реально в буфере. Вместо этого экран начинает показывать содержимое буфера, начиная с различных точек, и тем самым создается иллюзия сдвига. Этот метод называ­ ется аппаратным сдвиrом. Аппаратный сдвиг достигается за счет изменения стартовоrо адреса вывода. Это число, указывающее на символ в видсобуфере, который будет вывощ1тъся в левом верхнем углу экрана. Добав­ ление 80 к этому числу "сдвигает" весь экран на одну строку вверх, а вычитание 80 - на одну строку вниз. В режиме с 40 символами в строке надо вместо 80 прwбавлять или вычитать 40. На рис. 4.7 приведена диаграмма аппаратноrо сдвига. Отметим, что регистр стартового адреса не считает байты атри­ бутов, поэтому вы должны вычислять адреса памяти по-друrому, а не так, как Iipи прямом отображени11 в память. Имейте также в виду, что, несмотря на наличие разрывов памяти между грани­ цами страниц (96 байт между 80-символьными страницами· и 48 байт между 40"-символьными страницами), микросхема 6845 про­ пускает эти области и сдвиr непрерывно происходит с одной стра­ ницы на следующую. Аппаратный сдвиг происходит настолько
Вывод на терминал 281 быстро, что вам может оказаться необходимым вставить процедуру· за,-ержки, чтобы пользователь имел возможность увидеть, насколько сдвинулся экран. · BIOS хранит текущее значение регистра стартового адрес.• в переменной в своей области данных. Эта двухбайтовая переменная расположена по адресу 0040:004ЕН. Начало t "ам11ти Видеобуфер Стран~ца 1_ Страница 2 Страница З Страница 4 Конец пам11ти Строка 1 - - ------+ - -, 2-- - .. -.-------t-+-, з... .... 25 - ----+-+ ---__, 26 --- -L.-+-----"-1 27----_..____ _ .... ..... _____ _, Р~1с. 4.7. Апnарап11,1й, сд11~11· Низкий уровень 1Экранное изображение сдвигаетс11 вверх... J ··: по мере того, как начало страницы .сдвигаетс11 к боnьwим цресам пам11ти Стартовый адрес содержится в регистрах 12 (старший б.1йт) и 13 (младший байт) микросхемы 6845. В [4.1 .1] объясняется работа этой микросхемы. Прежде чем адресуемый· байт направляется ;в порт с адресом ЗDSH, необходимо послать номер адресуемого регистра в порт 3D4H. В дацном примере экран сдвигается вверх на одну строку. Переменная START_ADDR содержит адрес пер­ вого символа текущей верхней строки экрана. MOV BX,START_ADDR ADD ВХ,80 MOV DX,3D4H MOV AL,12 ;начинаем с на•~ала буфера • ;сдо~1rаем 111,1 1 СТJХЖу (ХО СНМIЮЛЩJ) ;оьшод о адресный реr~1стр ;адресуем ре1·нстр 12 .
282 Глава 4 OUT DX,AL ;посылаем запрос ,, INC DX ;теперь выiюднм u кома~щ11ыt'1 ре1·~1стр MOV AL,BH ;старшее сJю1ю 11 ЛL OUT DX,AL ;11осы;1аем е1-о 11 ре1·нстр 12 DEC DX ;обратно к адрес1юму рс1·~1стру MOV AL,13 ;адресуем ре1·истр 13 OUT DX,AL ;посылаем запрос INC DX ;снова командный регистр MOV AL,BL ;младшее слово в AL оuт DX,AL ;посылаем в регистр 13 •
Глава 5. Дисковые накопители 5.1 . . Управяение распределением ~ диска Все диски как гибкие, так и жесткие органи­ зованы одинаковым образом. 9оверхность диска разделена на ряд концентрических колец, называемых дорожками, а дорожки в свою очередь делятся радиально на сектора. Например, стан­ дартная дискета с диаметром 5 l / 4 дюйма имеет 40 дорожек, в системе MS-DOS 2.0 каждая такая дорожка разбита на 9 секторов (15 секторов на дискете емкостью 1.2 Мбайта и 17 - . на фикси­ рованном диске). Размер сектора - 512 байт, и 512 байт х 9 секторов х 40 дорожек х 2 стороны дает в итоге емкость дискеты 360:i<. Для всех типов дисков в MS-DOS •размер сектрра равен 512 байтам. Файл располагают на таком количестве секторов, которое необ­ ходимо, чтобы вместить ero полностью. Только несколько секторов на внешнем ободе дискеты зарезервированы для специальных нужд. Остальные щх:тупны на основе правила "первый подошел - первого обслужат''. Это означает, что по мере заполнения диска данными сектора постепенно заполняются по направлению к центру диска. При уничтожении файла сектора освобождаются и со временем свободные области становятся разбросанными по диску, разбивая новые файлы и замедляя доступ к ним для чтения и записи. Фиксированные диски имеют некоторые специальные характе­ ристики. Часто они состоят из двух или более параллельных пластин, у каждой из которых есть по две головки, чтобы читать с
284 Глава 5 обеих сторон. Все дорожки, расположенные на одинаковом рассто­ янии от центра, вместе называются цилиндром. Поскольку головки всех дисков двигаются тандемом, то можно достигнуть экономии перемещений, если заполнять все дорожки одного цилиндра, прежде чем переходить к следующему. Группы цилиндров могут относиться к различным операционным системам. Программа DOS FDISK может разбивать фиксированный диск на несколько разде­ лов (до четырех) разного ·размера. По этой причине параметры фиксированного диска могут сильно отличаться. Дисковые сектора определяются· магнитной информацией, которую записывает утилита форматизации диска. Информация включает идентификационный номер каждого сектора. BIOS нуме­ рует сектора 1-8, 1-9 или 1-15 в зависимости от емкости диска. Дорожки не маркируются, вместо этоrо они определяются механи- • чески по смещению головки чтения/ записи от внешнего края диска. Дорожки нумеруются от О до 39 для дискет диаметром 5 1/ 4 дюйма, а для дисков большей емкости их может быть больше. Дисковые функции BIOS обращаются к определенному сектору, указывая номера дорожки и сектора. Однако функции DOS рассматривают все сектора диска как одну цепь, которая нуме­ руется подряд, начиная от О, поэтому каждый сектор имеет свой логический номер. На дискетах первый сектор (дорожка О, сектор l) содержит запись начальной загрузки, которая является небольшой прог­ раммой, позволяющей компьютеру считывать с дискового накопи­ теля остальные части MS-DOS. Затем следvют две копии таблицы размещения файлов, которые содержат ~нформацию о распре­ делении дискового пространства (вторая копия хранится из сооб­ ражений безопасности). Затем идет корневой каталог, содержащий список файлов и ссылок на подкаталоги, а также указывающий, в каком месте диска они начинаются. Наконец, далее располагаются две небольшие программы DOS IВМВЮ.СОМ и IBMDOS.COM. Они считываются при старте и обеспечивают компьютер возмож­ ностями, необходимыми для нахождения и загрузки файла COMMAND.COM, который, н,есомненно, является основной частью операционной системы. Фиксированные диски имеют глав11ую запись заг11узки, вклю­ чающую таблицу разделов, которая позволяет распределить диск между несколькими операционными системами. Таблица разделов несет в себе информацию о том, где на диске начинается раздел DOS, а также первый сектор какого раздела содержит запись начальной загрузки. В остальном раздел организован так же, как и дискета.
Дисковые накопители 285 5.1 .1 . Чтение та611ицы размещения файлов Диск использует таблицу размещения файлов <F АТ) мя отве­ дения дискового пространства файлам и хранения информации о свободных секторах. Из соображений безопасности на всех дисках хранятся по две копии FAT. Они хранятся последовательно, в секторах с самыми младшими доступными логическими номерами, начиная со сторо"ы О, дорожки О, сектора 2 (сектор 1 занят записью начальной загрузки). Число секторов, занимаемых FАТ, определяется размером и типом диска. Отметим, что в MS-DOS 3.0 размер записи FАТ дЛЯ фиксированного диска может быть равен 16 битам. Здесь мы будем рассматривать только 12-битовые .записи; дЛЯ получения информации о 16-битовых записях смот­ ,рите Техническое руководство по MS-DOS. Таблица размещения файлов хранит информацию о каждом кластере секторов на диске. Кластер это группа стандартных сек­ торов размером в 512 байт (независимо от типа диска MS-DOS всегда работает с. 512-байтовыми секторами). Группировка секто­ ров необходима, чтобы уменьшить размер FAT. Однако большие кластеры, используемые· на фиксированном диске, напрасно расхо­ дуют дисковое пространство при записи маленьких файлов (ути­ лита размером 500 байт занимает 4К дискового пространства). Ниже представлены размеры кластеров и размеры FАТ для IВМ РС: • Тип диска дискета: 160К I80К 320К 360К 1,2М винчестер: IOM 20М Секторов 1111 к;щстер 2 2 8 4 Размер FЛТ 1 2 2 7 8 40 . При значительном размере кластера напрасно расходуется дисковое пространство, но когда большие .~tиски имеют малый размер кластера, то таблица размещения файлов очень разрас­ тается. При работе с дисками DOS загружает копию FAT в память, по возможности сохраня·я ее там, поэтому при большом размере FАТ может расходоваться мноrо оперативной памяти.
'286 Глава 5 Поскольку у машин АТ достаточно памяти, то для них приемлемы намного большие FAT. Поэтому, обеспечивая :жономию дискового пространства, для винчестера 20М взяты меньшие размеры класте­ ров, чем для lOM. Для дискет емкостью 1,2М выбран кластер размером в 1 сектор, так как их основное назначение состоит в хранении копий жесткого диска, а следовательно,. компактность очень важна. Каждая позиция в таблице размещения файлов соответствует определенной позиции кластера на диске. Обычно файл занимает несколько кластеров и запись в каталоге файлов содержит номер стартового кластера, в ко;гором находится начало файла. Просмот-, рев позицию FАТ, соответствующую первому кластеру, DOS нахо­ дит номер кластера, в котором хранится следующая порция файла, Этому кластеру соответствует своя запись в FАТ, которая в свою очередь содержит номер следующего кластера в цепочке. Для последнего кластера, занятого файлом, FAT содержит значения от FF8H до FFFH. Неиспользуемым (или освобожденным) кластерам соответствует значение ООО, а дефектным секторам - FF7H . Нако­ нец, значения от FFOH до FF7H приписываются резервным клас­ терам. Номер кластера содержит 3 шестнадцатиричные цифры, для хранения которых требуется 1 1/2 байта. Для умен1,шения разме­ ров FAT числа для двух соседних кластеров хранятся в трех после­ довательных байтах таблицы. MS-DOS автоматически производит все необходимые вычисления. Первые три байта FAT не испол~,зуются для номеров клас­ теров, Первый байт содержит код, опредсш1ющий тип диска (см, [1.1 .5 ]), а следующие 2 байта равны FFH. Поскольку эти позиции таблицы заняты, то кластеры нумеруются, начина~~ с 2, причем кластеры 2 и 3 занимают вторую тройку байтов та.блицы. MS-DOS 3.0 может создавать FAT с записями размером 16 бит. Такие записи необходимы для фиксированных дисков емкост1,ю более lOM, которые имеют больше, чем 4086 кластеров. На рис. 5.1 показана связь между FАТ и кластерами на диске. Очень редко возникают причины вносить изменения прямо в таблицу размещения файлов. MS-DOS заботится обо всех файло­ вых операциях и обеспечивает процедуры, анализирующие таб­ лицу ·для определения свободного пространства на диске. Однако для некоторых специальных целей, таких, как восстановление уда­ ленных ф_айлов или •написание драйвера блочного устройства, необходим прямой доступ к FAT. При прямом доступе к FAT надо соблюдать такие пр,1Вила. • Для нахождения следующего кластера файла: 1. Умножьте номер кластера на 1,5.
Дисковые накопители 287 2. Прочитайте 2 байта с полученным смещением (округляя вниз). 3. Если номер кластера четный, то ·возьмите младшие 12 бит, иначе возьмите старшие 12 бит. Для преобразования номера кластера в логический номер сектора: 1. Вычтите 2 из номера кластера. 2. Умножьте результат на число секторов в кластере. Стартовый номер КllаСтера Эnемент катаnога файn<1в ~--1F_1L_E_N_A_M_E_E_xт__ ·---31""4- Табnица раэмещени11 файлов Смещение дл11 кластера # 34 (1 1 / 2 байта/кластер) \ , , , C:t;j 1АIСО1,з1:;-;, ~ ~ 11147 ит.д: f Смещение ~ = 000000100100 ДЛII =36 кластера #36 = 00 0010010011 =147 Рис. 5.1 . Таблица размсщеш111 фай:ю11 Высокий уровень кластер #36 В следующем примере читается FAT и .определяется значение, хранящееся для кластера с номером 6. В f5.4.2 J объясняется начальный код, читающий сектора FA Т. Результатом является 12- бй:товое число, представленное в виде трех шестнадцатиричных цифр (по 4 бита каждая), возвращаемое в виде строки. В примере пары чисел, состоящих из двух цифр, объединсн~,а, и ·в качестве результата берутся правые или левые три цифр1>1. Когда Бейсик преобразует символ в шестнадцатиричную форму, то он возврс.1- щает только одну цифру, ·если первая была нулем, поэтому уда-
288 Глава 5 ленный нуль должен быть восстановлен, чтобы этот алгоритм работал правильно. • 100 ···чтение секторов FAT 110 DEFINT A-Z 120 DATA &HSS, &Н8В, &НЕС, &Н\Е, &/-188, &Н76, &НОС, &1188 130 DATA &НО4, &Н8В, &Н76, &НОА, &Н8В, &Н14, &Н8В, &1176 140 DATA &НО8, &Н8В, &НОС, &Н8В, &Н76, &НО6, &Н8Л, &ШС 150 DATA &Н8Е, &1-1D8, &Н8В, &НС3, &НВВ, &I-100, &1100, &IICD 160 DATA &Н25, &HS9, &НIF~ &HSD, &НСА, &НО8, &IIO0 170 DEF SEG = &HlOOO ·помещаем машшшый код с этого ·адреса 180FORN=ОТО38 190 READ Q: РОКЕ N,Q 200 NEXT 210 READSEC = О 220 BUFER =~2000 230 LOGNUM = 1 240 NUMSEC = 2 250 DRIVf: = О ·читаем 39 байт данных ·переносим их в память ·следующий байт ·,~ачинаем процедуру с 1-ro байта ·адрес буфера приема щ11111ых ·11ача.т1ы1ь1е сектора FAT ·2 сектора в FЛТ ·читаем 11акоrн-1тел1, А 260 CALL READSEC<ВUFER,LOGNUM,NUMSEC,DRIVE) ·•ш~:аем сектора 270 '"определяем номер следующего кт1стера файла 280 DEF ·SEG = , &Н2000 'буфер, где хра,~ится FЛТ 290 CШSNUM! = 6 ·кластер номер 6 300 С! = CLUSNUM! 310 С! = INТ (C!*l.5) 320 Х = РЕЕК(С!)' 330 У.= РЕЕК(С! + 1) 340 Х$ = НЕХ$(Х): У$ = НЕХ$(У) ·щ,лаем копию ·умножаем на 1,5 и округляем 'читаем 2 байта с этой познцщ1 ·персщ1дим в шсстнадцап1- ри•rные строки 350IFLEN(X$)= 1THENХ$ = "О" +Х$ делаем нх 2-шмвою,нымн 360IFLEN(У$) = 1THENУ$ = "О" +У$ 370И$=У$+Х$ ·объединяем •1исла 11 01111у строку 380 '"проверяем кластер 1ш четность 390 IF CLUSNUM! MOD 2 <> О THEN1 420 'уход. есш1 нечетный 400 NEXTCLUSТER$ RIGHT$(H$,3) ·если четный, то правые 3 цифры 410 GOTO 430 420 NEXTCLUSTER$ LEFТ$(H$,3) ·если нечетный, то левые 430 PRINТ NEXTCLUSTER$ ·печатаем результат Средний уровень Функция DOS ICH предоставляет информацию о таблице_ размещения файлов, но не саму FAT. Помссп~те номер накопи­ теля в DL,,rдe О = накопитель по умолчанию, 1 = А и т.д. При
Дискооые 11а1щпители 289 возврате DX содержит число кластеров в FАТ, а СХ - число байтов в· секторе. DS:BX указывают на байт, содержащий первый байт FАТ, т.е. на код, указывающий тип диска; эти коды пере­ числены в [l.1.5). Низкий уровень Намноrо. легче ·получить доступ к FАТ в языке ассемблера. Отметим, что умножение номера кластера на l ,5 производится копированием числа, сдвигом копии вправо на I бит для деления пополам и С..'IОж:tшием копии с оригиналом. Этот метод автома­ тически округляет результат вниз. Код, считывающий сектора FАТ в память, обсуждается в (5.4.2 ]. 1---в сегменте данных BUF DB 1024 DUP(0) ;---читаем FAT в память , LEA BX,BUF MOV DX,I MOV СХ,2 MOV AL,0 INТ 25Н РОР сх ;---получаем номер кластер,.1 MOV АХ,3 MOV СХ,А~ MOV DX,AX SHR DX,l ADD CX,DX ADD ВХ,СХ MOV DX,(BX] TEST АХ,! JNZ ODD_CL AND DX,OOOOllllllllllllВ JMP • SHORT СОNТ ODD_CL:MOV CL,4 SHR DX,CL СОNТ:. ;отводим место для 2 секторов ;указываем ва буфер да1111ых ;лоr•1ческий вомер сектора ;2 сектора ;накопитель А ;•1итаем сектора ;восстанавливаем стек· ;номер кластера в АХ ;де.rшем копию ;делаем вторую кош1~о ;делим вторую копию 11а 2 ;складываем между собо~1 ;добамяе~ как смещение ;полу•1аем 2 байта из этого м~ста ;номер кластера• нечетный? ;уход, если да ;получаем номер ;уход через обработку не•1етного ;подготовка к сдвигу вправо ;tдвиmем вниз старшие 1'i бит ;теперь с,ледующий кластер в DX S.1.1. Оnредеnенне дoctynнoro днсковоrо пространства Хотя в следующем ситуацию при ошибке лучшеrо "лекарства", 10 Р. Джордейн параграфе объяснено, как восстановить из-за нехватки места на диске, но нет чем предусмотрительность. Программа
290 1лава .-, должна контролировать доступное дисковое пространство и сооб­ щать пользователю о нехватке места. Если места недостаточно, то пользователь может выйти из программы и устранить проблему без потери информации. Высокий уровень Следующая ассемблерная подпрограмма возвращает в перемен­ ную CLUSTERS число свободных кластеров на указан\юм диске. Надо поместить номер накопителя в DRIVENUM, где 1 = А, 2 = В и т.д. В приложении Г объясняется, как ассемблерные подпрог­ раммы включаются в программы на Бейсике. 10 DEFINT A-Z 20 DRIVENUM = 30 CLUST = ·0 ~•спол~,зуем целые перемс1111ые ,.., .. ·сюда помещаем номер накопителя шшциаш1з~1руем персме,шую 40 DATA &Н55, &Н8В, &Н:ЕС, &Н88, &Н76, &НОб, &Н88 50 DATA &Н14, &Н84, &НЗб, &HCD, &Н21, &В88, &IOR 60 DATA &НО8, &Н89, &HID, &HSD, &НСА. &Н04.-&НОО \ 70 DEF SEG = &HIOOO помещаем под11ро1'j)амму 80FORN=ОТО20 90 READ Q: РОКЕ N,Q 100 NEXT 110 FREESP = О 120 CALL FREESP(CLUST,DRIVENUM) 130 PRINT " ~LUSTERS: ";CLUST Средний уровень 'берем каждый байт 111,паем el'() 1,1 ПОМСlt(аСм II llaM)ПI» ·указатель на н~Р1ало процедуры вызов процедуры ·печать числа кластеров Функция 36Н прерывания 21Н сообщает, сколько имеется свободного пространства на диске. Единственный входной регистр - DL, который должен содержать номер накопителя. Накопитеш, по умолчанию обозначается О, накопитель А - 1 и т.д. При возврате ВХ содержит число доступных кластеров, АХ - число секторов в кластере, а СХ - количество байтов в секторе. Небольшое упраж­ нение· в умножении дает желаемый результат. В следующем примере проверяется, что на двухсторонней дискете осталось по меньшей мере 2К дискового пространства: MOV АН,36Н MOV DL,I INT 21Н ;номер функции ;накопитель А ;получаем информацию
Дисковые накопители 291 СМР ВХ,2 ;имеется ;ш 2 с1юбод11ых кластера'! JL RUNNING_OUT ;если 11ет, то сообщаем об лом S.1 .3 . Полученне/установка размера фанла 1 Программе может понадобиться проверить размер файла по разным причинам. Одна из возможных причин состоит в опреде­ лении числа записей, содержащихся в файле. Друrая - в опреде­ лении позиции конца файла, с тем чтобы файловый указатель был установлен верно для добавления в файл новых данных без изме­ нения существующих. Конечно, размер файла устанавливается автоматически функ­ цией DOS. Иногда программа может нуждаться в резервировании дискового пространства для дальнейшего использования. В :этом случае надо открыть файл в режиме прямого доступа и вывести такой номер записи, чтобы файл имел достаточную длину. Записи между "фиктивной" и реально относящимися к файлу будут запо.Jfнены теми данными, которые случайно окажутся в дисковых секторах, отв~денных для файла при этой операции.' Высокий уровень В Бейсике функция LOF (длина файла) возвращает точное число байтов,- отведенных файлу (преду.gреждаем, однако, что старые версии Бейсика - 1.х - возвращают число 128-байтовых блоков, используемых файлом). Файл должен быть уже открыт и ссылаться на него надо по номеру, под которым он был открыт. Формат Х = LOF(l). В следующем примере определяется, сколько 64-байтовых записей содержится в файле, открытом как #3: НЮ OPEN "FILENAME" AS #3 ·открьшаем файл 110 RECORDLEJ\i = 64 ·оr1ре11еляем дшшу записи 120 NUMBREC = LOF(З)/1{ECORDLJ;:N ·11ы•шслясм число записей Средний уровень FCB функция 23Н прерывания 21 Н сообщает число записей в файле. Если приписать файлу длину записи в I байт, то его размер будет возвращен в байтах. DS:DX должны указывать на управляющий блок. открытого файла. Затем вызовите функцию. Если файл не найден, то в AL возвращается FF. В противном случае в AL возвращаетсS): О, а число записей помещается в поле номера записи прямого доступа FCB (байты 33-36). Для ·правиль- 10* !
292 Глава 5 ной работы поле длины записи •FCB должно быть установлено после открытия файла, но перед вызовом функции; это двух­ байтовое поле расположено по смещению 14 в FCB. Если размер файла неточно делится на длину записи, то сообщаемое число записей округляется вверх. Вот пример, в котором используется длина записи, равная 1: ;---определение размера файла LEA DX,FCB ;DS:DX указывает на FCB _ MOV BX,DX ;копируем указатель в ВХ MOV СХ, 1 ;размер записи в СХ MOV [ВХ] + 14,СХ ;пишем в поле размера записи FCB MOV АН,23Н ;функция, сообщающая размер файла lNT 21Н ;вызов функции моv АХ,[ВХ] + 33 ;получаем младшую часть размера файла MOV СХ,(ВХ] + 35 ;получаем старшую часть размера файла Можно·также устанавливать длину файла, применяя управля­ ющий блок файла. Для этого надо использовать функцию записи блока с прямым доступом, которая обсуждается в [5.4 .5 ]. У этой функции имеется частный случай, когда число выводимых записей устаt1авливается' равным нулю, то длина файла устанавливается равной числу записей, указанному в поле записи прямого доступа. Метод, использующий дескриптор файла (file handle) не имеет функцииft которая непосредственно сообщала бы длину файла, однако имеется возможность вычислить размер, передвинув указа­ тель файла с его начала на конец. При открытии файла указатель файла автоматически устанавливается на первый его байт. Указа­ тель файла перемещается функцией 42Н прерывания 21Н. Надо поместить в AL кодовое число 2, направляющее указатель на - конец файла. В ВХ должен быть указан номер файла, а CX:DX содержит смещение от конца файла до позиции, 1в которую должен быть установлен указатель, поэтому поместите О в оба этих регистра. Затем вызовите функцию. При возврате DX:AX будет содержать новую позицию указателя относительно его предыдущей позиции, т.е. будет содержать длину файла (DX содержит старший байт). При возникновении ошибки будет установлен флаг переноса, а в АХ будет возвращена 1, если неправилен номер функции, и 6, если неправилен номер файла. Не забудьте затем снова вернуть указатель на начало файла, если это необходимо. Поместите О в AL, СХ и DX и вызовит~ функцию снова. Продемонстрируем сказанное на примере:
Дисковые накопители ;---открываем файл LEA DX,FILE_PATH MOV AL,0 MOV АН,ЗDН 1NТ 21Н JC OPEN ERROR - ' ;DS:DX указывают на путь .1< файлу ;открываем файл для чтения ;функция открытия ф.,йла ;открываем ero ;проверка на ошибку MOV НANDLE,AX ;запоминаем номер ф.,йла ;---определяем длину фаt'sла MOV АН,42Н ;функция перемещения указателя MOV AL,2 ;код установки на конец файла моv моv моv 1NТ JC МО\( MOV BX,НANDLE сх,о DX,0 21Н POINTER_ ERROR FSIZE_HIGH,DX FSIZE_LOW,DX ;номер файла в ВХ ;ОвСХиDX ;сдвигаем указатель ;ошибка? ;запоминаем размер файла 293 S.1.4. Восстановnенне пос:пе оwнбок, связанных с нехваткой пространства на диске При попытке записи на цолный диск может произойти сбой ,программы. Часто легко избежать этого, даже в Бейсике, проверив. предварительно наличие дискового пространства (см. (5.1 .2 ]) . Однако если ошибка все же произошла, то постарайтесь дать пользователю возможность исправить ее. Позвольте ему сохранить только часть данных или стереть какой-нибудь другой. файл и повторить попытку. Или, еще более радикальное средство, позвольте пользователю вставить другую дискету. Последний подход должен реализовываться с большой осторожностью. Сначала закройте все открытые файлы. Затем выдайте запрос на смену дискеты. После того как пользователь &ообщит, что новая дискета вставлена, создайте новый файл и запишите туда данные. Высокий уровень В Бейсике надо установить процедуру обработки ошибок, как показано в (7.2 .5). Если оператор Бейсика делает попытку писать на полный диск, то возвращается код ошибки #61. При этом управление может быть· передано ·процедуре обработки ошибок, которая информирует щщьзователя о проблеме и позволяет ему справиться с ней, не теряя данных.
294 Глава 5 100 ON ERROR GOTO 5000 ·разрешаем обработку ошибок 200 OPEN FNAME$ FOR OUTPUT AS #1 ·открываем ф.'lйл 210FORN = 1ТОARRLEN ·на11инаем писать массив на диск 220 PRINT # 1, ARRAУ$ (N) •записываем один элемент 230 NEXT ·следующий 5000 IF ERR 5100 IF ERR 61 THEN 6100 ·диск полон? ·друrие ошибки ... 6100 '"восстановление пр~1 переп0Jiнен~1и диска 6110 ВЕЕР: PRINT "Disk full - choose an option:" 6120 PRINT "(А) - Re-edit the file" 6130 PRINT "(В) - Delete some other file from disk" 6140 PRINT "(С) - Use different diskette" (здесь идет процедура восстановления) 6500 RESUME Средний уровень Все фун1щии DOS, которые пишут на диск, выдают опреде­ ленный код ошибки при попытке записи на полный диск. Приведем сводку этих кодов: МеТО8 8~Па ~НК!!ИЯ Название Код ошибки FCB 15Н Последователышя Зс'\пись ЛI, = FCB 22Н Прямая зап~:~сь ЛL=1 FCB 27Н Прямая запись блока ЛL=1 Дескр~штор 40Н Запись в фаr1л/устройст1ю сх<>пх Проверка на ошибку должна делаться после каждой записи на диск. И если критической ошибки не происходит, то восстанов­ ление не вызывает -проблем. Надо только проверять ·на ошибку каждый раз, когда вы вызываете одну из этих функций, и создать надежную процедуру обработки ошибок по вашему вкусу.
Дисковые накопители 295 5.1. Работа с ката11оrамн днска Каждый диск имеет один корневой каталог, с которого начинается поиск всех остальных каталогов. Корневой каталог может содержать элементы, указывающие на подкаталоги, которые в свою очередь могут содержать ссылки на другие подка.: талоги, образуя древовидную структуру каталогов. Корневой ката-· лог всегда расположен в •определенных секторах диска; подката­ логи хранятся как обычные дисковые файлы, поэтому они могут быть расположены в любом месте диска. Отметим, что если фиксированный диск разбит на разделы, он может содержать до четырех корневых каталогов, хотя MS-DOS "видит" только один корн:евой каталог. Катало~;и имеют различные размеры в зависимости от размера диска и его разбиения на разделы. В следующей таблице прив.едены размеры и позиции корневых каталогов для разных типов дисков: Тип диска диск~та 160К 180КI 320К ЗбОК l,2M жесткий диск 1ОМ 20М Разме~ каталоI-а Число :мемсIIто11 На11алы1ый сск1·012 4 сектора 4 сектора 7 секторов 7 секторов 14 секторов 64 64 112 112 224 __ ;~ - - --- -пе реме1111ые--------- ----------nеременI1ые--------- \ 9 9 15 15 29 В зависимости от разбиения на разделы фиксированный диск может иметь различные размеры. каталога и номер начального сектора. 1 Если весь диск отведен для MS-DOS, то на ХТ и АТ под корневой каталог отводится 32 сектора, что позволяет имет1, в нем 512 элемЬнтов. Как корневой каталог, так и подкаталоги используют 32 байта для хране~ия информации об одном файле, независимо от типа диска. Таким образом, в каждом секторе может храниться инфор- • мация о 16 элементах каталога. Каждое 32-байтовое поле разбито следующим образом:
296 байты 0-7 8-10 11 12-21 22-23 24-25 26-27 28-31 имя ф.-.t1J1a р:1с11шреш1с файJ1а атр~1бут файт1 з:~резср1шрош11ю Глава 5 11ремя послед11е1u доступа к ф.-.r1J1y дата 1юслед11еrо доступа к файлу 11а11.алы-11.1й кластер размер файJщ Точка между именем файла и его трехбайтовым расширением не хранится. Все поля выравнены по левой границе, а пустые байты заполняются пробелами (код ASCII 32). Атрибут фа~ла определяет, является ли файл спрятанным, защищенным от записи и т.д. (см. (5.2.6 ]). Он определяет также специальные элемент1,1 каталога, такие, как подкаталоги или метка тома. Информация о времени и дате упакована, поэтому для чтения этих значений требуются битовые операции (см. [5.2 .5)). Начальный кластер указывает на позицию в таблице разме­ щения файлов (FAT), которая обсуждалась в [5.1 .1 ]. FAT хранит информацию о свободном пространстве на диске, а также отводит сектора при записи файла. FАТ отводит дисковое пространство порциями, большими чем 1 сектор, ко,:орые называются класте­ рами. Файл расположен в цепочке кластеров, и FАТ содержит соответствующую цепочку элементов, указывающих, где эти клас­ теr расположены на диске. Каталог должен указывать на началь­ но звено цепочки элементов файла в FAT, и эта информация содержится в поле на'tальный иомер кластера. Поскольку файл обычно не целиком занимает·последний отведенный ему кластер, то поле размер файла хранит точную длину файла в байтах. 5..1 .1. Чтение/нэменение корневоrо катаnоrа 1 Каталоm диска подразделяются на корневой каталог (обсужда­ емый здесь) и подката.доги (обсуждаемые в 15.2.3 ]). Когда пользо­ ватель программы вводит для работы имя какого-либо файла, то бывает полезным проверить, имеется ли этот файл на самом деле. Обычно изменения в корневом каталоге производятся в ходе обыч­ ных файловых операций или с помощью специал1,ных функций DOS. Однако можно ра~отать с каталогом напрямую. Необхо­ димость в таком подходе возникает при работе на языках высокого уровня,_ где утилиты DOS по большей части недоступны. ' Корневой каталог читается и изменяется при загрузке его в память с помощью .метода, показанного в 15.4.2 ], когда читаются
Дисковые накопители 297 абсолютные сектора диска. В этом случае не остается места между секторами, когда они загружаются в память. Буфер, содержащий данные сектора, может рассматриват~я как набор 32-байтовых полей и пара указателей, которые могут использоваться для движения по каталогу. Один укщштель всегда кратен 32 и указы­ вает на начало :элемента каталога. Второй указатель добавдяется к первому и указывает на одно из полей в 32-байтовом элементе. Данные в памяти могут быть изменены требуемым образом, а затем весь буфер записывается обратно на дйск. Имеются два метода чтения абсолютных с;екторов диска, и в обоих случаях только одно число отличает чтение от записи. Поскольку ошибка при записи на диск может легко повредить все содержимое диска, то надо действовать аккуратно. Сначала убеди­ тесь, что операцря чтения сеvтора выполнена· верно во всех отно­ шениях. После этого можно попробовать записать на диск, взяв точную копию кода, использованного для чтения, и заменив только номер функции. Высоt<ий уровень Бейсик выводит каталог по команде FILES. При этом выво­ дятся только имена файлов. FILES выдает каталог накопителя по умолчанию; для указания накопителя напишите FILES "А:" и т.д. Можно потребовать, чтобы была выведена информация об отдель­ ном файле, написав FILES ''A:MYFILE.DAT". Как и в операци- • онной системе, имя файла может содержать • и ?. Оператор FILES снабжает информацией пользователя, н~ иногда наличие некото­ рого файла "хочет" проверить программа. В этом случае надо открыть файл для последовательного чтения и, если он не сущест­ вует, возникнет· ошибочная ситуация. Смотрите обсуждение и пример в [5.2.З ]. Для поисJ(а любой информации, относящейся к корневому каталогу, используйте процедуру_ на машвнном языке, приве­ денную в [5.4.2 ]. После того как данные каталога помещены в паr,tятъ, установите указатели, как описано выше, и ведите попек по буферу памяти с 32-байтовым интервалом. В приведенном ниже примере показан поиск элемента каталога, относящегося к стер­ тому файлу. Когда файл стирается, первый байт имени файла заменяется на ESH, но все остальное содержимое данного элемента остается неизменным. Конечно, при этом освобождается дисковое пространство, отведенное файлу в FАТ.•Процедура восстановления удаленного файла должна знать номер начального кластера в FAT. В примере этот двухбайтовый номер кластера помещается со смещением 26 в элементе каталога.
298 Глава 5 100 "'чтение секторов каталога в память с сегмента &Н2000 11 О INPUT "Enter erased filen_ame ", FNAM$ ·запрос имени файла 120 IF LEN(FNAМ$) > 12 THEN ВЕЕР: GOTO 110 ·ошибка 130 IF INSТR (FNAM$,". ") > 9 THEN ВЕЕР: GOTO 11 О •ошибка 140 '"дополнение имени и расширеция файла нулями 150 У = INSТR(FNAМ$,".") 'цроверяем положение точки 160 IF У = 0 THEN FIRPAR$ = FNAМ$: GOTO 230 'если типа нет, то вперед 170 ЕХТ$ = LEFТ$(FNAМ$, LEN(FNAМ$) - У) ·выделяем тип 180 ЕХТ$ = ЕХТ$ + SТRING$(3 - LEN(EXT$),"") 'добавляем пробелы 190 FIRPAR$ = RIGHT$(FNAМ$,Y - 1) 200 FIRPAR$ = FIRPAR$ + SТRING$(8 - LEN(FIRPAR$),"") 210 FNAM$ = FIRPAR$ + ЕХТ$ 220 '"теперь хотим найти удаленный файл 230 MID$ (FNAM$, 1, 1) = CHR$ (&НЕ5) 240 DIRPТR = О 250 FIELDPТR = 26 260FORN=1ТО112 270Х$="" 280FORМ=ОТОIО 290 Х$ = Х$ + PEEK(DIRPТR + М) 300 NEXT ·заменяем первый символ ·указатель на элемент ·указатель на номер кластера ·на дискете 112 элементов ·чистим Х$ ·читаем имя файла из каталога 'берем по символу ·следующий 310 IF Х$ = FNAМ$ THEN 340 ·совпадает с введенной строкой 320 NEXT ·если нет, то следующий 330 PRINT "Тоо late - File entry oЫiterated": END не найден 340 Х = PEEK<DIRPТR + FIELDPТR) 'нашли его, берем 1-й байт и 350 У = PEEK(DIRPТR + FIELDPТR + 1) '2-й байт номера кластера 360Z'=Х +256*У ·теперь номер кластера в Z Средний уровень MS-DOS обеспечивает две пары функций для поиска файлов: одна - для файлов, открытых методом управляющих блоков файла, а другая - для файлов, открытых методом дескриптора файлов. Одна из функций каждой пары ищет первое появление имени файла в каталоге, а другая - посл~ующие появления, когда в имени файла содержатся "джокеры". Только метод, исполь­ зующий дескриптор файла, позволяет искать подкаталоги. Метод FCB. Функция l lH прерывания 21Н ищет первое появ­ ление файла. ·Установите DS:DX на неоткрытый FCB и выполните *Джокер - это· карта, которая может заменить любую другую по желанию ее обладателя. В вычислительной. технике имеются два вида "джокеров": ?, заменяющий ровно один любой символ, и •• заменяющий любое количество любых символов. - Примеч. пер.
Дисковые накопители 299 функцию. При возврате AL будет содержать О, если файл найден, и FF _, в противном случае. DTА заполняется информацией из каталога. Для обычных FCB первый байт DTА содержит номер накопителя (1 = А и т.д.), а следующие 32 байта содержат элемент каталога. Для расширенного FCB первые' 7 байт файла копируются в первые 7 байт расширенного FCB, восьмой байт ука­ зывает на накопитель, а следующие 32 байта - элемент каталога. ;---в сегменте данных FCB DB l,'NEWDATABAК',25DUP(0) ;---ищем файл MOV AH,ilН LEA DX,FCB INT 21Н СМР AL,0 JNE NO_FILE LEA BX,DTA ;функция поиска в каталоге ;указываем на FCB ;ищем ;успешно? ;если нет, то действует процедура обработки ошибки ;теперь DS:BX указывает на элемент каталога После' функции llH можно использовать функцию 12Н для поиска следующих подходящих элементов, когда имя файла содер­ жит "джокеры". В данном случае в имени файла допустим только символ "?", но не "*". Эта функция работает точно так же, как и первая, и если найден второй файл; то информация о первом файле в DTА будет уничтожена повторной записью. Метод дескриптора файлов. Функция 4ЕН прерывания 21Н ищет файл с данным именем. DS:DX должны указывать на строку, представляющую путь к файлу. Например, B:\EURO- PE\FRANCE\PARIS указывает на файл PARIS. Строка может содержать до 63 символов и завершаться символом ASCII О. Имя файла может содержать "джокеры", включая как "?", так и "*". Поместите атрибут файла в СХ; если он обычный, то О, в против­ ном случае проконсультируйтесь в [5.2 .6] относительно значений атрибута. При возврате устанавливается флаг переноса, если файл не найден. Если файл найден, функция заполняет DTА информаци~й о нем. Ниже приведен частный случай применения DTА с методом дескриптора файлов, хотя обычнб DTА используется функциями MS-DOS для работы с помощью FCB. Первые 21 байт DTA заре­ зервированы DOS для поиска следующих совпадающих файлов. Двадцать второй байт соответствует атрибуту файла, за ним идут два байта, содержащие время, и еще два байта - дату. Следующие 4 байта содержат размер файла, начиная с младшего , слова. И наконец, дается имя файла в виде строки переменной длины,
300 Глава 5 4 байта содержат размер файла, начиная с младшего слова. И наконец, дается имя файла в виде строки переменной длины, заканчивающейся байтом ASCII О. Точка (ASCII 46) разделяет имя и расширение, и ни один из этих элементов нс заполнен пробелами. ;---в сегменте данных РАТН DB 'B:FRANCE\PARIS\4EME·, o ;---ищем файл МОУ АН,4ЕН LЕЛ DX,PЛTIJ МОУ сх.о INT 2\Н JC NO FILE LEA BX,DTA МОУ AL, [ВХ] + 21 ;1юмер функ~~ш• ;DS:DX указывают на пуп, ;об1,1 1 111ый атр11бут файла ;11щсм файJ1 ;уход, ссл11 не 1шйде11 ;DS:BX указы11ают 1ш ОТЛ ;теперь атр11бут файла 11 ЛL Поиск следующего появления имени файла (когда исполь­ зуются "джокеры") проводится с помощью функции 4FH преры­ вания 21Н. Она подготавливается так же, как и функция 4ЕН, при этом указатель DT А не должен менят1,ся. Если других совпадений не найдено, устанавливается флаг переноса, а в АХ появляется 18. 5.2 .2. Созданне/удаnенне подкатаnоrа Программа м_ожет создавать или удалять подкаталоги тол1,ко ири выполнении некоторых условий. Для создания подкаталога необходимо, чтобы было по крайней мере одно пустое место в корневом каталоге, а для удаления подкаталога - чтобы он не содержал файлов или ссылок на другие nодка~:алоги. Кроме того, вы не можете удалить подкаталог, который является вашим теку­ щим каталогом (т.е. тот, с которым по умолчанию в1,111олю1ются все операции над каталогами). Отмстим также, что невозможно удалить корневой каталог. Высокий уровень Бейсик предоставляет команды MKDIR (создать каталоr) и RMDIR (удалить каталог),. после чего должны следовать стандар­ тные пути указания каталога, содержащие до 63 символов, вклю­ чая имя накопителя. Путь должен быть заключен в кавычки. Что-
Дисковые накоn!,1-тели 301 бы добавить подкаталог с именем STORKS в подкаталог BIRDS, напишите MKDIR "B:MAMMALS\BIRDS\STORKS''. После выполнения этой команды будет ·создан файл STORKS, исполь­ зуемый как подкаталог, и факт его существования будет отражен в создании элемента с именем STORKS в подкаталоге с именем BIRDS. Для удаления этого подкаталога надо сначала удалить из него все файлы (см. [5.3 .2 }) . Затем надо использовать команду RMDIR "B:MAMMALS\ВIRDS\STORKS". В этих примерах предполагалось, что вашим текущим ката­ логом являлся корневой каталог. Однако если он находится rд , то на пути к подкаталогу, над которым осуществляются операции, то нет необходимости указывать весь путь. Поэтому если вашим текущим катало~;ом является ·BIRDS, то для создания или удаления подкаталога STORKS можно использовать команды MKDIR "\STORKS" или RMDIR "\STORKS". Средний уровень Поскольку управляющие блоки файлов обслуживают тол1,ко корневой каталог, для создания ми удаления подкаталога надо использовать дескрипторы файлов. , Создание подкаталога. DS:DX должны указывать на строку, дающую накопитель и путь к каталогу, в КQтором необходимо соз­ дать подкаталог. Строка должна завершаться б,tй'fом ASCI I О. Для открытия подкаталога с именем PRIMATES в корневом каталоге накопителя А: записывают строку в виде "A:\PRIMATES". Для открытия подкаталога в другом подкаталоге с именем MAMMALS напишите "A:\MAMMALS\PRIMATES". Имя накопителя А: может быть опущено, если вы работаете с накопителем, исполь­ зуемым по умолчанию, и путь может начинаться с текущего ката­ лога. IJоместите· в АН 39Н и выполните прерывание 21 Н; если указан правильный путь, то будет создан новый каталог. В противном случае· будет установлен флаг переноса, а АХ содержать код ошибки 3 (путь неверен) или 5 (нет доступа). В приведе~ном примере создается подкаталог PRlMA TES: ;---в сегме,пе данных РА1:Н DB 'A:MAMMALS\PRIMATES ,О ;---создаем подкаталог с именем PRIMATES LEA DX,PATH ;DS:DX должны ука:1I,шап, на путь MOV АН,39Н ;11омер функцнн INT 21Н ;создаем подкаталог JC _ERROR_ROUT ;обработк,1 ошибок
302 Глава 5 Удаление подкаталога. Для удаления подкаталога надо сформи­ ровать строку, полностью совпадающую с той, которую вы указы­ вали при создании каталога. Затем поместите в АН ЗАН и выпол­ ните прерывание 21 Н. Опять при невыполнении функции в АХ будут возвращены коды 3 или 5 (код 5 указывает на то, что каталог непустой). • S.1.3 . Чтение/изменение подкаталога Подкаталоги во многом подобны корневому каталогу, за исключением того, что они хранятся как обычные файлы, а не в заранее определенных секторах. Подкаталоги невозможно спутать с обычными файлами, поскольку объект каталога, относящийся к подкаталогу, имеет специальный байт атрибутов (с установленным битом 5, см. [5.2 .6 ]). Подкаталоги начинаются с двух специал1,ных 32-байтовых объектов, первый из которых имеет имя "точка", а второй - "две точки". Они ориентируют подкаталог среди окружа­ ющих каталогов. Ссылки на подкаталоги нижнего уровня записы­ ваются как обычные ссылки на файлы. Предполагается, что подкаталог может быть прочитан как любой другой файл, поэтому, казалось бы, не составляет труда загрузить его в память. Но, к сожалению, создатели MS-DOS поместили О в поле длины файла для элементов, относящихся к подкаталогам. В результате DOS считает, что этот файл имеет нулевую ~Jiину, и отказывается читать его. Нет простого способа преодолеть эту проблему. Высокий уровень В Бейсике команда FILES может использовать стандартные имена путей для вывода подкаталога; например, FILES "B:MAMMALS\BIRDS" выводит все файлы, содержащиеся в подкаталоге BIRDS. Эта команда может быть применена и для получения информации о наличии в каталоге определенного файла. Например, FILES "LEVELI \NEWDATA" ищет файл NEWDATA и выводит его имя, если он найден. Хотя это может быть полезным для пользователя, но часто самой программе необ­ ходимо знать, существует или нет указанный файл. Чтобы устано­ вить это, попытайтесь открыть файл для последовательного чтения. Если он не будет найден, то возникнет ошибочное условие 63. Создайте процедуру обработки ошибок, как описано в [5.4.8 ]. Затем используйте переменную, чтобы отмет,ить, был ли найден требуемый файл (в нашем примере переменная "EXISTS"). Если
Дисковые накопители 303 нет необходимости, чтобы этот файл оставался открытым, то закройте ero перед тем, как двинуться дальше. 100 ON ERROR GOTO 1000 'процедура обработки ошибок 110 EXISTS = 1 't1ачальное значение "флага" 120 INPUT "Enter filename: ",S$ ·запрос имени фама 130 OPEN S$ FOR INPUТ AS #3 'открываем ero для последующего чтения 140 IF EXISTS = О ТНЕN ВЕЕР: PRINT "File does not exist" lOOO'IF ERR 53 ТН:ЕN 1500 }010 IF ERR 64 THEN ... 1500 EXISTS = О 1510 RESUME 140 Средний уровень •файл не существует? •другие ошибки ·меняем значение флага 'продолжаем выполнение программы Функции, применяющие метод дескриптора файлов, которые использовались для доступа к корневому каталогу (см. [5.2.1 ]) , могут так же просто обращаться к любому подкаталогу. Чтобы вывести все -содержимое каталога, надо использовать функцию 4ЕН для поиска файлов *·*• а затем повторять поиск, применяя функцию 4FH. Когда искомых файлов больше не останется, то устанавливается флаг переноса, а AL будет содержать 18. Каждый раз при обнаружении очередного элемента в DTA будет записы­ ваться,информа,ц11я о файле~ включая полный путь к нему (отме­ чаем использование ОТА в функциях, работающих ~ дескриптором файла). В следующем примере выводятся полные пути ко всем обычным файлам подкаталога. ;---в сегменте данных РАТИ DTAH DВ ,A:~AMMALS\ •,•·,О DB 256 DUP(?) ;---установка DTA .. LEA DX,DTA MOV AH,lAH INT 2Ш ;---ищем первый файл MOV АН,4ЕН LEA i>X,PATH ;DS:DX указывают на DTA ;функция уста~юоки DТЛ ;устанаw~ипнем DTA щомер функции ;указыпаем 11а строку пути
304 MOV СХ,О INT 21Н JC ERROR ;---выводим имя файла NEXT_LI: LEA BX,DTA ADD ВХ,30 NEXT_CH: MOV DL,[BX] СМР DL,0 JE END STR MOV АН,2 INT 21Н ;тQЛько нормальш,1е атрибуты ~ищем *.* ;обработка ошибок ;ВХ указывает на DTA ;смещеш,е для имеш1 файла ;получаем симnол из име1ш ;проверка на конец строк~~ ;выход, ес;ш конец ;и1шч~ - UЫЩ)Дl,,IМ (l,,IMIIOJI INC ВХ ;увеличиваем указатель JMP SHORT NEXT СИ ;следующий с~1мвол ;---возврат каретки/перевод строки в конце строки END_SТR: MOV АН,2 MOV DL,13 INT 21Н MOV DL,10 INT 21Н ;---ищем следующий файл ;фу11кция вывода символа ;код возврата карстк~, ;11ыподим ;код перевода строки ;вывод~,м LEA DX,PATH ;указьшаем на строку пути MOV AH,4FH ;номер фу11кцш1 INT 21Н ;ищем следующ~,й файл JC FINISH ;если нет, то выход JMP SHORT NEXT LI ;иначе - выводим имя файла FINISH: 5.1 .4. Полученне/установка текущего каталога Глава 5 Текущий каталог это каталог, в котором DOS ищет файл, если к нему не указан путь. Если не установлено противного, то теку­ щим каталогом является корневой каталог. Высокий уровень Бейсик устанавливает текущий каталог с помощью команды CHDIR. За командой следует строка, указывающая путь к ката­ логу, на который надо перейти. Строка может содержать до 63 символов, включая имя накопителя, и должна быть заключена в кавычки. CHDIR "C:MAMMALS\PRIMATES\GIВBONS" делает
Дисковые иакопители 305 подкаталог GIВBONS текущим каталогом. Чтобы перейти в корне­ вой каталог, напишите CHDIR "\" или CHDIR "В:\". Бейсик версии 3.0 может сообщать путь к текущему каталогу, как . это делает команда DOS РАТН. Введите PRINT ENVIRON$("PATH"). Средний уровень· Функция 3ВН прерывания 21 Н устанавливает текущий ката­ лог. DS:DX должны указывать на путь к каталогу в стандартном виде, и эта строка .1.tuлA..1ia заве(?шаться байт.ом ASCII О. Например, .B:BIRDS\PARROTS\POLLY делает POLLY текущим каталогом. В:. может быть опущено, если это текущий накопитель по умол­ чанию (см. (5.3. l ]) . Чтобы сделать текущим корневой каталог накопителя А:, напишите А:\. В предЛагаемом примере текущим каталогом устанавливается POLLY: ;---в сегменте данных РАТИ DB ·в:ВIRDS\PARROTS\POLLY'.0 ;---делаем POLLY текущим каталогом МОУ АН,ЗВН ;номер функции , LEA DX., P AT H ;DS:DX долж111,1 ука:t1,11111п, щ1 nут1, INT 21Н ;уста11ш1ш111асм текущМ·1 катало,· Чтобы определить, какой каталог является текущим, надо • использовать функцию 47Н прерывания 21 Н .. DS:SI должны указывать на область данных разttером 64 байта, в которую будет записан путь. В DL указываетсs\ накопитель, причем О • "по умолчанию", 1 - А, 2 = В и т.д. Функция возвращает строку без имени накопителя. Если был указан несуществующий накопитель, то В' AL возвращается код ошибки 15. Строка 1-1ачинается с имени первого подкаталога цепочки, а не с обратной косой черты. Байт ASCfl О сиmализирует • о ~онце строки. В данном примере имя текущего каталога присва'hвается переменной ~•CURRENT_DIR": ;---в сегменте данных CUR_DIR DB 64 DUP(?) ;---получить текущий каталог MOV АН.4,7Н ;1юмер функции LEA SI,CURRENT_DIR ;указьшаем 1ш область да11111,1х MOV DL,l ;накопитель А INT 21Н ;помещает строку по адресу DS:SI
306 Глава 5 S.1 .S. Поnученне/установка временн н даты посnеднеrо доступа • к файnу Если отсчитывать от нуля, то байты 22-23 32-байтового элемента каталога .содержат время последнего доступа к файлу. Байты 24-25 содержат дату. Значение битов следующее: Время: биты 11-15 часы !0-23) 5-10 минуты (0-59) 0-4 секунды (0-29 с дпухсску111111ым и1пср11алом) Дата: биты 9-15 rод !0-119, СМСЩСШIС с 191Ю юда) 5-8 месяц ( 1-1 2) 0-4 число (1-31) День недели не записывается; DOS вычисляет его по остальной информации. Отметим также, что, как обычно, младший байт двухбайтовых значений расположен в памяти раньше, чем старший. Средний· уровень Метод доступа к файлу с использованием управляющего блока файла позволяет получить дату последнего достущ1 .к файлу, но не время. Когда FCB открывается функцией 0FH прерывания 21 Н, то заполняется двухбайтовое поле даты в приведенном выше формате. Это поле расположено в FCB со смещением 14Н (см. (5.3.5]). С другой стороны, доступ к файлу с помощью дескриптора файла позволяет как получить, так и установить дату и время пос­ леднего доступа к файлу. Функция 57Н прерываli,ИЯ 2IH выпол­ няет все операции. Для установки времени и даты поместите номер файла в ВХ и О в AL. Для получения даты и времени надо поместить в AL l. В обоих случаях дата содержится в DX, а .время - в СХ. Значение битов совпадает с тем, что дано в таблице. В техническом руководстве по MS-DOS у;верждается, что младшие байты информации находятся в СН и DH, и наоборот. На самом деле это не так. При возникновении ошибки устанавливается флаr переноса, а в АХ возвращается 1, если в AL указано неправильное число, и 6, если неверен дескриптор файла. В следующем примере определяется час, в который был отмечен последний доступ к файлу: ;---в сегменте данных РАТИ DB 'B:NEWDATA.BAК',0 ;---открываем файл
Дисковые накопители 307 LEA DX,PATH ;указываем 11а строку пути MOV АН,ЗDН ;функция открытия файла MOV AL,0 ;открываем для чтс11ия INT 21Н ;открываем файл JC OPEN_ERROR ;переход на обработку ошибки ;---получа~м дату и время доступа к файлу MOV ВХ,АХ ;помещаем номер файла в ВХ MOV AL,0 ;код для чтения времени MOV АН,57Н ;номер функции' INT, 21Н ;получаем время доступа JC ТIME_ERROR ;переход на обработку ошибок ;---сдвигаем биты, относящиеся к часам, в начало СИ MOV CL,3 ;готовим сдвиг SHR CH,CL ;теперь СН содержит •шс доступа 5.1.6 . Спрятанные н защнщенные от запнсн файnы DOS использует шесть различных атрибутов файлов, которые дают данному файлу определенный статус. Файл может иметь несколько атрибутов одновременно ·(но не все). Атрибуты устанав­ ливаются 12-м байтом 32-байтовоrо элемента каталога. Значение имеют младшие шесть битов, а остальные должны быть равны нулю. Биты таковы: • если бит 5 1, файл был изменен со време11и последней архивации 41, файл является подкаталогом 31, этот элемент является не файлом, а меткой тома 21, файл является "системным" \ 11, файр спрятан при поиске по каталогу о1, файл_ объявлен только для чтения Бит 5 - это архивный бит, используемый программами BACKUP и RESTORE DOS. Этот бит сбрасывается в О после архи­ вации и устанавливается, когда с файлом снова работали. При сле­ ду10щей архивации неизмененные файлы могут быть обнаружены и проигнорированы. Высокий уровень Бейсик не позволяет вам напрямую устанавливать атрибуты файла. Справьтесь в [5.2.1 ], как считать каталог в память, найти нужный файл, сделать изменения и снова записать его на диск.
308 Глава 5 Как только каталог помещается в память, байты атрибутов нахо­ дятся по смещениям 11, 43, 75 и т.д. При необходимости вы можете прочитать текущие атрибуты и изменить только один бит, используя технику битовых операций, описанную в приложении Б. Но легче просто переписать все атрибуты заново. Буд;,те внима­ тельны, ошибки могут быть фатальными. В данном примере считываются атрибуты файла с именем "NEWDATA.AAA": 100 ·читаем сектора каталога, начиная с &Н2000 и затем ... 110 DEF SEG = &Н2000 ·указывнем на область каталога 120 FILENAМE$ ,;, "N E W DA T A A A A" ·ищем имя файла без то•Iки 130 DIRPТR = О указатель в каталоI,е 140FORN=1ТО112 •п ров еря ем в се элементы 150Х$="" ·временная строка для ~IмешI фай;1а 160FORМ=ОТО10 'для каждого символа имени 170 Х$ = Х$ + PEEK(DIRPТR + М) ·добавляем его к строке 180 NEXT 190 IF Х$ =; FILENAME$ THEN 220 ·если имя найде,ю, выходим 200 NEXT 210 PRINT "Fiie not fo11nd": END 220 Х = РЕЕК (DIRPTR + 11) ·нет такого файла ·получаем атрибуты 11уж11оrо файла 230 IF Х AND 32 <> О THEN PRINT "File поt baked нр" 240 lF Х AND 16 <> О THEN PRINT "File is а sнbdirectory" 25_0 IF Х AND 8 <> О ТI-IEN PRINT "Volнmc laЬcl - поt а filc" 260 IF Х ЛND 4 <> О Tl-lEN l'RINT "l "ilc is а systcm filc" 270 JF Х AND 2 <> О Tl\EN PRINT "l "ilc is а hiddcn Гilc" 280 IF х· AND 1 .<> О THEN PRINT ''l'ile is ,·cad-onl,•" . . Средний уровень Функция 43Н прерывания 21Н может как находить, так и изменять атрибуты файла, но только если фа-йл был открыт с помощью метода дескриптора файлов, а не с помощью •v1етода управляющего блока файла. Аналогичной функции для FCB нет. Байт атрибутов может быть установлен при создании файла (см. [5.3. -2 ]) при использовании расширсщюrо управляющего блока файла. Но если вы последовательно откроете FCB, измените уста­ новку атрибутов и затем закроете файл, то у него останутся первоначальные атрибуты. Хотя., конечно, вы можете изменит~, атрибуты каким-нибудь обходным путем, но намного проще применить функцию, использующую метод дескриптора файлов.
/ . Дисковые накопители 309 Чтобы присвоить файлу байт атрибутов, содержащийся в СХ (на самом деле в CL, поскольку СН равен 0), поместите I в AL Можно, наоборот, поместить в AL О, чтобы в СХ _был возвращен текущий байт атрибутов файла. В обоих случаях DS:DX должны указ.,~:вать на строку, представляющую путь к файлу. Конец строки отмечается байтом ASCII О (который не входит в число 63 символов). В предлагаемом примере статус "hidden" (спрятанный) присваивается файлу OVERDUE: -- ;---в сегменте данных .РАТИ DB 'A:ACCOUNTS',0 ;---включаем признак спрятанного файла MOV АН,43Н ;номер функции MOV Af...0 ;читаем б.1йт атрибутов LEA DX,PATH ;DS:DX указывают на путь INT 21Н ;байт атрибутов в СХ JC ERROR_ROUТINE ;обработка ош~1бок OR CL,I0B МОУ АН,43Н MOV AL,I INT 21Н ;включаем бит 1 ;номер функцш1 ;заменяем байт атр~1бутов ;тепер1, фai:'u1 стал спрятn~шым Флаг переноса устанавливается при возникновении ошибки. В . этом случае в АХ возвращается 2, если файл не найден, 3, если не· - найден путь, и 5 - при других ошибках (нет доступа). S.2.7 . Чтенне/нзмененне метки тома Метка тома для дискеты - это элемент каталога, имеющий специальный атрибут. Метка занимает первые 11 байт элемента, относящиеся к имени и расширению файла. Байт атрибутов по смещению 11 содержит значение 8 (бит 3 "" 1). Поля времени и даты заполняются обычным образом. Одним из свойств этого атри­ бута· является то, что данный элемент не выводится по команде DIR. • Метка может занимать любую позицию в каталоге. Она ищется перебором всех байтов атрибутов, пока не будет найдено значение 8. Чтобы стереть метку., надо просто поместить ES в первый байт соответствующеrо элемента; сам байт , атрибутов можно не менять. Для изменения метки необходимо з..1писать новые 11 символов (остаток заполняется пробелами). Ес.,,и вам понадобится присвоить метку тома диску, который ее не имел, то
310 Глава 5 надо только найти пустое место в каталоге и записать туда метку и соответствующий атрибут. Высокий уровень Обсуждение в [5.4 .2] объясняет, как читать и писать абсолют­ ные сектора в Бейсике. Для стандартной двухсторонней дискеты надо использовать номер стороны О, номер дорожки О, номер сек­ тора 6 и число секторов для чтения/записи - 7 . После того как данные записаны в отведенный буфер, для изменения или добав­ ления метки тома можно воспользоваться примерами, приведен­ ными здесь. Затем сектора должны быть перезаписаны на диск. Будьте внимательны, ошибка может привести к потере всей информации на диске. В следующем примере показаны поиск метки тома и ее изменение: 100 ·сектора заrруже11ы, 11а•шная, скажем, с &Hl000 110 DEF SEG = &НIООО 120 DlRPTR = 11 ·указатею, Iia байт атрибутов 130FORN= 1ТО112 ·проверяем псе элемсIпы каталоI11 140 IF PEEK(DIRPТR) = 8 THEN 180 ·111,1ход, есшI метка тома 150 DIRPТR = DIRPTR + 32 ·указываем на следующий элемент 160 NEXT проI~еряем его атр~1бут 170 PRINT "No volume label foнnd": END метк~I нет 180 INPUT "Eпter new volнme laЬel", У$ ·запрос метк~• 190 IF LEN (У$) > 11 THEN ВЕЕР: PRINT "11 chars only": GOTO 180 200 У$ ,;с У$ + SТRING$O l-LEN (V$),32) ·догюл11яем пробелами 210 DIRPТR = DIRPTR - 11 ·11оз11ращасмся iш II11'~а1ю элемсIпа 220FORN = 1ТОLEN(V$) 230 РОКЕ N,MID$(V$,N,l) 240 NEXT Iюмещасм 11се симI1ою,I метк~• 25() 'теперь осталось перезаписать сек1·ора на диск Низкий уровень В приведенном ниже примере предполагается, что вы создали буфер данных размером 3584 байт для хранения всех семи секто­ ров каталога дискеты емкостью 360К. Буфер называется DIR AREA. В первом примере метка тома ищется и выводится или;- если она не найдена, выводится сообщение об се отсутствии. Для удобства область буфера отводится в сегменте данных; лучше отвеqи память для задачи, а затем освободить ее (см. [l.3.11).
Дисковые накопители ;---в сегменте данных , VOL_SТR DB The volume laЬel is $' NO_LAВ DB ·тhere is no volume laЬel $' DIR_AREA DB 3584 DUP(?) ;---читаем 7 секторов каталога '- MOV AX,SEG DIR_AREA ;сегмент буфера MOV ES,AX MOV BX,OFFSET DIR_AREA ;смещение буфера· MOV DL,0 ;номер накопителя MOV DИ,О ;номер головки •MOV СИ,О ;номер дорожки MOV CL,6 ;стартовый сектор MOV AL,7 ;число секторовJкаталоrа MOV АИ,2 ;номер функции чте11ия INT IЗИ ;читаем каталог в память ;---ищем метку тома, сравнивая. байт атрибутов с 8 ТRY_AG: MOV СХ,112 ;число элементов ADD MOV СМР JE ВХ,11 AL,[BXJ AL,8 GOT_IT ;смещеш1е для атрибутов ;берем 1-й элемент ;это метка тома? ;если да, то в1,1ход ;шш•1е, 11!1 следующий элемент LOOP TRY_AG ADD ВХ,32 ;---выводим сообщение об отсутствии метки тома MOV АИ,9 ;фу11кц11я ш,шода строки LEA DX,NO_LAB ;указываем 11а строку INT 21Н ;выводим ее JMP SИORT CONT ;11а конец ;---вi.1водим строку, выдающую метку тома • GOT IТ: MOV АН,9 ;функцш1 11ывощ1 строки LEA DX,VOL_SТR INT 21Н SUB ВХ,11 MOV СХ,11 MOV АИ,2 NЕХТ_СИ: MOV DL,[BXJ INT 21Н INC вх LOOP NEXT_ СИ CONT: ;указываем 1ш строку ;выводим ее . ;указ.·пе.,11, 11а метку . ;пишем 11 сим.волов ;функция вы1юда симuолов ;симuол 11 DL ;оывод11м. CIIMDOJI ;переходим к сдедующему 311
312 Глава 5 Чтобы стереть метку, поместите следующий код в GOT_IТ: GOT_IT: MOV AL,0ESH ;код отметки пустого элемеIпа SUB ВХ,11 ;устанавливаем указател1, 1Iа нача;ю ;mеме1па MOV [ВХ] ,ЛL ;меняем ~!ервыii байт Для того чтобы изменить метку тома, необходимо восполь- • зоваться в GOT IT следующим кодом (предполагается, что вы уже подготовили 11-байтовую строку NEW_LAB): GOT_IT: LEA S1,NEW_LAB SUB ВХ,11 MOV DI,BX' MOV СХ,11 REP MOVSB ;SI доджен указып.'lп, Iш строку ;ВХ указывает на 1шчадо метки ;помещаем указатель в DI ;пересыдка 11 символов ;пересылаем строку Для создания метки применяют тот же самый код, но в этом случае надо установить байт атрибутов равным 8. (Вы можете 1 просто добавить ASCII 8 к строке, содержащей новую метку, так как байт атрибутов следует непосредственно за самой меткой.) И наконец, во всех случаях изменения каталоrа необходимо записать его обратно на диск. При этом ошибки допускать нел~,зя. ;---запись изменен"1'1х секторов обратно на диск M()V AX,SEG DIR_AREA ;регистры, как и пр~, 1пе11ш1 , MOV ES,AX MOV BX,OFFSET DIR_AREA MOV- DL,0 MOV DH,0 MOV СН,О MOV CL,6 MOV AL,7 MOV АН,3 ;номер фу11кLLИ~1 заш1с~1 секторов INT IЗН 5.3 . Подrотовка к работе с файламн В программах, написанных на языках высо­ кого уровня, можно легко открыть файл - вся подготовительная работа для операций с файлами в этом случае будет выполнена
Дис1'овые на1'оnители 313 автоматически. Однако программисты на языке ассемблера должны создать специальные области дан!{ЫХ, которые используются при операциях ввода/вывода. MS-DOS использует два метода доступа к файлам: метод управляющего блока файла (FCB) и метод деск­ риптора файла. Метод FCB существует с тех пор, когда MS- DOS еще не работала с древовидной структурой каталогов, по:)тому с его помощью можно получить доступ только к файлам, находя­ щимся в текущем каталоге. Метод дескриптора файла позволяет получить доступ к любому файлу, независимо от того, какой ката­ лог является текущим. Поскольку теперь древовидная структура каталогов широко используется, то метод FCB становится анахрони·змом, но MS-DOS продолжает поддерживать этот метод, чтобы сохранить совмести­ мость со старым. программным обеспечен11ем, и по этой причине мы рассмотрим и его. Однако в своих программах всегда старай­ тесь применять метод дескриптора файла. Метод дескриптора файла имеет дополнительное преимущество в том, что он требует меньше подготовительной работы, хотя в некоторых приложениях сами операции ввода/ вывода при этом могут оказат1,ся более сложными, чем в методе FCB. Например, операции чтения файла с прямым доступом с иtпользованием метода дескриптора файла. требуют, чтобы программа вычисляла смещение каждой записи в файле 1 в то время как соответствующая функция FCB получает номер записи и делает необходимые вычисления сама. Прежде чем ,читать илй писать данные в файл, его нужно открыть. Открыть файл это значит создать и инициализировать специальную область данных, используемую MS-DOS, которая содержит важную информацию о файле, такую, как имя файла, имя накопителя, размер записи файла и т.д. Языки высокого уров­ ня, такие, как Бейсик, создают эти области автоматически. Одной из таких областей является управляющий блQк файла 1:1, когда используется метод FCB, программа создает этот блок, а MS-DOS читает и манипулирует его содержимым. Первоначально FCB содержит только имя фа,йла и имя накопителя; после того как файл открывается, в него добавляется информация о размере записи и текущей позиции, ( которой к нему будет осуществляться доступ. При доступе к файлам с помощью дескриптора файла MS-DOS автоматически создает область данных для файла в произвольном месте. Затем MS-DOS создает уникальный 16-битовый код номера файла; впоследствии этот "номер" используется функциями DOS для идентификации того, с каким из открытых файлов произво­ дится операция. Все, что нужно для нахождения файла, - это стан­ дартная строка пути, в которой может быть необязательное имя накопителя, а имена подкаталогов разделены обратной косой
314 Глава 5 чертой. Эти строки отличаются от стандартного запроса MS-DOS только тем, что они должны завершаться байтом ASCII О, чтобы программа могла найти конец строки (такие строки называются строками ASCIIZ). Идентификация файла Установка начальной точки дл11 чтени11/записи Установка ~мела байтов дл11 чтени/1/запмси Пол11 накопителя, имени Номер файла в ВХ (полученный при и расширени11 FCB открытии файла, мс• польэу11 строку ASCIIZ) {((текущий блок х 128) + (текуща11 запись)) х Установка Qразмерзаписи файлового Вычисл11етс11 из номера указател11 записи пр11мого доступа Размер записи Установки (можно читать/писать числа байтов несколько записей) вСХ Рис. 5.2 . Два метода доступа к файлам Операции по пересылке данных из файла или в файл требуют, чтобы была указана область памяти, в которую или из которой будут направщ1ться данные. Такой буфер определяется отведением ему места в памяти и установкой укttзателя на его первый байт (т.е. на младший адрес буфера в памяти). Если передано слишком много данных, то буфер переполняется и может разрушит~, данные, расположенные в следующих адресах памяти. Буфер может использоваться как промежуточный, работающий только с небольшой порцией данных для операций чтения или записи. Буфер также может помещаться в область памяти, в которой прог­ рамма действительно хранит и обрабатывает данные. Функции доступа через управляющий блок файла определяют промежуточный буфер с помощью указателя, который все время хранится операционной системой. Этот буфер называется областью обмена с диском (disk transfer area) или DTA. К сожа­ лению, в технической документации по IВМ РС часто термином DTА называют указатель на буфер, хотя на самом деле правильно называть его указателем на ОТ А. После того как указатсл1, на DTA установлен с помощью специальной функции, все файловые операции используют его до тех пор, пока он нс будет изменен. Функции же, использующие метод дес1<риптора файла, должны указывать стартовый а~рес буфера обмена каждый раз при вызове
Дисковые накопители 315 функции, указатель на DT А они игнорируют, в отличие от функ­ ций управляющего блока файла. Рис. 5.2 показывает два метода доступа к файлам. S.3.1 . Установка/проверка накопнтеnя по умоnчанню Программы могут экономить часть работы, назначая накопи­ тель, на котором содержатся файлы данных, по умолчанию. Если в начале выполнения программа запросит у пользователя, с каким накопителем он будет работать, то впоследствии у нее нс будет сомнений, к какому накопителю следу~т обращаться. Высокий уровень В приведенной программе на Бейсике текущий накопитель по умолчанию переключается с помощью процедуры на машинном языке. Процедура имеет длину 7 байт. Она помещается в строку Х$, а переменная Z служит указателем на первый байт процедуры. В приложении Г объясняется, как вставлять ассемблерные проце­ дуры в программы на Бейсике. Номер накопителs1 устанавливается в строке 110, причем О = А, 1 = В и т.д. Если назначенный по умолчанию накопитель - несуществующий, то ошибки не будет, поэтому будьте внимательны. Не пытайтесь объединить строки 120 и 130 этой процедуры, поскольку в этом случае .интерпретатор Бейсика будет обрабатывать их неправильно. 100 DEF SEG 110 NUM ;_, О ·сегмент на начало области Бейсика ·выбираем IшкоIнпе;11, А 120 Х$ = CHR$(180) + CHR$(14) + CHR$O 78) + ПIR$(NUM) + CHR$(205) + CHR$(33) + CHR$(223) • 130 У = VARPТR(X$) ·полу•шем дескр~~птор строки (адрес 11 У+ 1) 140 Z = РЕЕК(У + 1) + РЕЕК(У + 2)*256 оы•Iисляем адрес строки 150 CALL Z ·выполняем машинную процедуру Средний уровень Функция ЕН прерывания 21 Н устанавливает накопитель по умолчанию. Надо просто поместить номер накопителя· (0 = А, 1 = В и т.д.) в DL и выполнить прерывание. Эта функчия возвра­ щает в AL число накопителей на машине. Отметим, что когда у машины имеется только один накопитель, то возвращается число 2. Лучший способ· определения числа накопителей у машины описан в [1.1 .5].
316 MOV АН,ОЕН MOV DL,l INТ 21Н Глава 5 ;номер функции ;код для 11акопнтеля В ;устанавлш1аем 11ако11нтель 110 умол•~а11ню Функция 19Н прерывания 21Н сообщает, какой из накопи­ телей является накопителем по умолчанию. Для этой функции нет входных регистров. При возврате в AL содержится кодовый номер, гдеО=А,1_ = Вит.д. S.Э.2. Соэданне/удаnенне файла Можно создать файл, не помещая в него никакой информации. В этом случае создается элемент каталога, а длина файла устанав­ ливается равной О. При удалении файла соответствующий элемент · каталога на самом деле не удаляется, он просто становится недействующим: за счет замены первоrо байта элемента (первого ·символа имени файла) на ESH. Впоследствии этот элемент может быть перезаписан при создании нового файла. Во время удаления файла вносятся также изменения в таблицу размещения файлов, с тем чтобы сектора, используемые этим файлом, были доступны для других файлов. Само содержимое секторов при этом не стира­ ется. Поэтому можно восстановить удаленный файл, однако преду­ преждаем, vто операции с таблицей размещения файлов надо производить очень осторожно. Высокий уровень Бейсик не имеет специальной команды для создания файла. Вместо ;этого при открытии файла указанное имя/ ищется в ката­ логе, и если оно не найдено, создается новый файл. Если открыть новый ф,;1йл, а затем закрыть ero, не производя в него записи, то он останется в каталоге с длиной I байт, и ему будет отведен кластер дискового пространства (единственный байт - символ Ctrl-Z - ASCII 26, который используется в качестве признака конца стандартного ,:екстовогр файла). Детали оператора OPEN ·см. в (5.3.З ]. Оператор CLOSE файл не уничтожает, для этой цели служит оператор KILL. Для того чтобы уничтожить файл, не надо ero открывать. Просто поместите имя файла в кавычках, например KILL "A:ACCOUNT.DAT". И]!и, если файл находится в другом подкаталоге, надо использовать стандартный путь к файлу, напри­ мер KILL "A:\FINANCES\ACCOЦNT.DAT". В обоих случаях имя накопителя необходимо указывать, только если файл нахо­ дится не на накопителе, принятом по умолчанию. Отметим, что
Дисковые накопители 317 вы не можете воспользоваться этим методом, чтобы удалить подкаталог (который является одним из видов файла) , - в этом случае применяйте RMDIR. Средний уровень Файл может быть создан или, уничтожен с помощью либо метода управляющего блока файла, либо метода дескриптора файла. Создание фа~а одним из перечисленных методов ни в коей мере не ограничивает будущий доступ к нему только этим методом. Но поско.(lьку одновременно с созданием он открывается, то при создании необходимо использовать тот метод, с помощью которого вы будете работать с этим файлом на этот раз. Когда файл создается, а затем закрывается и при этом в него ничего не записывается, ему соответствует элемент каталога с нулем в поле длины файла, однако дисковое пространство этому файлу не отво­ дится. Важно понимать, что когда последовательный файл откры­ вается для записи (а не для добавления) данных, то используется именно эта функция создания файла, поэтому файл обрезается до нулевой длины и затем полностью перезаписывается. Метод FCB. Функция 16Н прерывщшя 21Н создает и откры­ вает файл. Создайте FCB с именем файла и накопителя и пусть DS:DX указывают на него. Затем вызовите функцию. Если во время просмотра каталога найден совпадающий элемент, то он будет повторно использован для записи информации о создаваемом файле, уничтожая тем самым сведения о старом файле. Чтобы избежать непреднамеренного разрушения файлов, сначала проверьте наличие файла с таким именем с помощью функции l lH прерывания 21Н (см. [5.2.1 ]). Если в каталоге' файла с таким именем нет, то создается новый элемент и в AL возвращается О, если каталог полон, то - FF. Чтобы присвоить файлу специальные атрибуты (например, статус только для чтения [5.2.t) ]) , исполь­ зуйте расширенный упрi!вляющий блок файла [5.3.5 J; При откры­ тии новый файл инициализируется с нулевой длиной и ему отво­ дится кластер дискового пространства. Например: ;---в сегменте данных FCB DB l,'MYFILE DAT,25 DUP(O) ;---проверка наличия такого файла MOV AH,I IH ;функция поиска файла LEA DX,FCB ;DS:DX указывают на FCB INT 21Н СМР AL,0 ;ищем файл . ;AL = О, если файл существует
318 JE WARN USER ;если да, то сообщаем об :этом ;---создание файла MOV АН,16Н INT 21Н ;номер функЦ~IИ СОЗдашtя файла ;создаем файл Глава 5 Для создания файла со специальными атрибутами, например статусом только для чтения, надо использовать расширенный управляющий блок фай:ла. (Байт атрибутов файла обсуждался в [5.2 .6 ].) К обычному FCB надо добавить 7-байтовый заголовок, начиная с байта FFH, далее следуют 5 байт ASCIJ О, а затем нужный байт атрибутов. Для создания спрятанного файла необхо­ димо, чтобы был установлен бит l байта атрибутов. Чтобы спря­ тать файл, открытый в приведенном примере, напишите: FCB DB 0FFH,S DUP(0),2,1,'MYFILE DAT,25 DUP(O) Функция IЗН прерывания 21Н уничтожает файл. В этом слу­ чае надо, чтобы DS:DX указывали на неоткрытый FCB и только затем выполните функцию. Если файл с указанным именем не найден, то в AL возвращается FF, иначе - О. В имени файла могут использоваться "джокеры" (знаки вопроса, но не "звездочки"), тогда за одно обращение к функции может быт~, удалено несколько файлоJ3. Например: ;---в сегменте данных FCB D8 l,'MYFILE DАТ',25 DUP(0) ;---удаляем файл MOV АН,IЗН LEA DX,FCB INT 2IH СМР AL,0FFH JE DEL_ERROR ;номер функции удаления файла ;DS:DX указывают на !'СВ ;удаляем файл ;проверка на ошибку ;уход на обработку 0tш1бк~1 Метод дескрnптора файла. Функция ЗСН прерывания 21 Н создает и открывает новый файл методом дескриптора файла. DS:DX должны указывать на строку, представляющую пуп, к файлу и имя файла в стандартном формате MS-DOS, включая имя накопителя, если файл находится не на накопителе, принятом по умолчанию. Строка должна завершаться байтом ASCII О. Байт атрибутов файла (см. [5.2 .6 ]) поместиtе в СХ (О - для обычного файла). Затем выполните функцию. При успешном выполнении флаг переноса будет равен нулю, а в АХ буд~т возвращен _номер нового файла. При возникновении ошибки флаг переноса устанав-
Дисковые накопители 319 пивается в 1, а в АХ содержится код ошибки, который может бьiть равен З, если не найден путь, 4, если уже открыты все буфера для файлов, и 5, ·если каталог полон или файл уже существует со статусом только для чтения. Отметим, что если в каталоге уже существует файл с таким именем, то он "обрезается" до нулевой длины, и тем самым разрушаете~. Во избежание. ошибок надо предварительно использовать функцию 4ЕН нрерывания 21 Н для проверки-. ;---в сегменте данных РАТИ DB 'B:LEVELl \LEVEL2\FILENAME.EXT' .О ;---проверка наличия файла в каталоге МОУ АН,4ЕН ;функция поиска в каталоr'е LEA DX,PATH ;DS:DX указывшот на путь INT 21Н ;проверка 11али•шя файла JNC WARN_USER ;если есть, 'ГО сообщаем ;---создание файла МОУ АН,ЗСН М:ОУ СХ,О . INT 21Н JC OPEN_ERROR МОУ HANDLE,AX ;функция создаш1я -файла ;нормалы1ые атрибуты ;создаем файл ;уход 11а обработку оuтбки ;за1юм~шаем номер ф,1йла В MS-DOS версии 3.0 добавлена новая функция дл·я создания файла методом дескриптора файла. 'Это функция 5.ВН прерывания 21Н. Она работает точно так же, как и описанная выше функция ЗСН, за исключением того, что она возвраща1.:т расширенные коды ошибок, что позволяет лучше обрабатывать ошибочные ситуации. Они объяснены в f7.2.5 ]·. / Для уничтожения файла методом дескриптора файла испол~,­ зуйте функци;<J 41 Н прерывания 21 Н. И опять DS: DX должны уК'азывать на строку, дающую пуп, к файлу,_ ~1 его имя. "Джокеры" в именн файла не допускаются. Затем вызовите функ­ цию. Если при возврате флаг переноса установлен, то функция не выполнена, в этом случае AL будет содержать 2, если файл не найден, и 5, если произошла ошибка на диске. Отметим, что с цомощью этой функции вы не можете удалить файл со статусом только для чтения; измените атрибуты файла (см. [5.2 .6 ]) перед его удалением. Например: ;---в сегменте данных РАТИ DB 'B:LEVELI \LEVEL2\FILENAME.EXT ,О ;---уничтожаем файл MOV АН,41Н ;номер функции уш111тожеш1я
320, Глава 5 LEA DX,PATH ;DS:DX указывают на путь INT 21Н ;уничтожаем файл JC DELETE_ERROR ;уход на обработку ошибки MS-DOS версии 3.0 имеет специальную фующию (5АН преры­ вания 21Н) для создания временного "безымянноrо" файла. Опе­ рационная система сама генерирует имя для файла и проверяет, что такого файла еще нет в каталоге. При этом и-:ключается вся­ кая возможность того, что при создании временного файла будет разрушен существующий файл с совпадающим именем. При входе DS:DX должны указывать на строку пути к каталогу, в котором должен быть создан временный файл. Строка завершается обрат­ ной косой чертой. Поместите атрибуты файла в СХ (обычно 0). При возврате АХ будет содержать номер файла, если только флаг переноса не установлен, в этом случае АХ содержит информацию об ошибке. Произвольное им.,я файла добавляется к концу строки пути. Эта функция может возвращать расширенные коды ошибок, которые существуют только в MS-OOS 3.0; они объясняются в (7.2.5]. Файл, созданный этой функцией, не, уничтожается автома­ тически - программа должна использовать функцию 41Н (см. выше). В предлагаемом примере программа создает временный файл, а затем уничтожает ero: ;---в сегменте данных РАТН DB ·в:LEVELI\LEVEL2\',12 DUP(O) ;---создаем временный файл MOV AH,SAH ;11омер функции LЦ DX,PA~H ;DS:DX указыо.'lют 1ш путь INT 21Н ;создаем временный файл JC CRE_ERROR ;уход на обработку ошибки MOV АН,41Н LEA DX,PATH ;номер функц~1и ;DS:DX указывают 1111 11уть INT 21Н ;уш1•1тожаем uремеш1ый файл JC DEL_ERROR ;уход 11а обработку ошибк~1 S.3.3. Откр~.1тне/ закрытие файла "Открыть" файл - это значит создать небольшие блоки памяти; где будет содержаться информация о файле. Они будут служить промежуточной станцией (буфером), через которую данные будут передаваться между файлом и памятью. Язы1<и высокоrо уровня
Дисковые накопители 321 автоматически создают для вас эти блоки, а язык ассемблера - нет. При открытии файла каталог проверяется на ero наличие. Если файл найден, то MS-DOS берет информацию из каталога о размере и дате созL(ания файла. Затем, при закрытии файла, система обновляет информацию в каталоге. Закрытие файла также очищает все системные буфера переноса, посылая на диск остав­ шуюся информацию. Если вы не закроете файл перед завершением программы, то это может привести к потере данных. Когда программа работает со многими файлами, надо постоянно помнить о том, сколько файлов открыто одновременно. MS-DOS 2.1 позволяет иметь до 99 одновременно открытых фай­ лов, причем по умолчанию - только 8 (измените это число с помощью команды MS-DOS FILES). Бейсик позволяет одновре- _ менно открывать не более 15 файлов. Каждый файл занимает место для блока параметров и буфера. Память для каждого файла отводится ·отдельно перед тем, как он открывается, и потому недоступна для программ, даже если указанное число файлов не используется в настоящий момент. По этой причине вы можете экономить память с помощью описанного метода, устанавливая максимально допустимое число открытых файлов именно таким, которое вам требуется. Высокий уровень При открытии файла в Бейсике идет поиск в каталоге, и если файл не найден, то создается новый файл с данным именем. Име­ ются два способа записи оператора открытия файла и в большин­ стве случаев они эквивалентны. Единственное отличие, состоит в том, что один из них более закодирован, в то время как другой ближе к естественному языку. В обоих операторах вы долж11Ы указать по меньшей мере три вида информации. Во-первых, требу­ ется имя файла, и поскольку это строка, оно должно быть заклю­ чено в кавычки. Во-вторых, число, начиная с l, присваивается Ф<!йлу как идентификационный номер, по которому другие опера­ торы читают или пишут в файл. И в-третьих, вы должны указать, для какой цели открывается данный файл, т.е. открыт ли он для прямого или для последовательного доступа. Чтобы открыть файл MYFILE. ТХТ для записи в последовательный файл (файл будет иметь номер 2), запишите или , OPEN "0",#2,"MYFILE.TXТ" или OPEN "MYFILE.TXT" FOR OUTPUT AS #2 11 Р. Джордейн
322 Глав;.~ 5 Отметим, что в обоих случаях номер 2 относится к буферу файла #2. Число может быть любым, не превосходящим коли­ чества разрешенных буферов для файлов. Если поддерживается одновременная работа с шестью файлами, то число должно бып, в интервале от О до 6. Однако буфер файла с номером I нс обяза­ тельно использовать раньше, чем файла с номером 2. По умо.1- чанию Бейсик устанавливает число буферов, равное 8, но вы можете изменить его на другое (от 4 до 15). Из :лих файлов четыре используются Бейсиком для своих нужд, поэтому по умол­ чанию только 4 файла доступны вам для ввода/вывода. Для того чтобы установить число доступных буферов, используйте параметр F: при запуске Бейсика. Например, если вы при старте Бейсика напишете BASICA/F:l0, то будет создано 10 буферов, шесть из которых доступны для файловых операций. Второй параметр, S:, устанавливает размер буферов файла. Вес буфера имеют один и тот же размер. По умолчанию берется размер, равный 128 байтам, однако допустимы размеры до 32767 байт. Для файлов последовательного доступа этот размер может быть установлен равным О, что позволяет немного с:жономить память. Для файлов прямого доступа он должен быть нс мен1,шс максимального размера записи. Отмстим, что если размер записи равен 512 байтам и размер буфера также равен 512 байтам, то это приводит к ускорению дисковых операций. Команда BASICA/S:512/F:10 открывает 10 буферов размером 512 байт. Каждый файл требует 188 байт плюс размер буфера, поэтому для такой конфигурации потребуется 7К памяти. Число буферов нс может быть больше, чем разрешено иметь открытых файлов в DOS. Кодированная форма. Первая из форм оператора OPEN использует одну букву для обозначения желаемого типа операций над файлом. Имеется три возможности: "О" - открыть файл с последовательным доступом дли вывода; "1" - открыть файл с последовате.1ь11ым доступом для 1шода; "R" - открыть файл с прямым доступом для вr~ода/11ыоода. Последовательные файлы не моrут записываться, коrда они открыты для чтения, и наоборот. В типичных случаях последова­ тельные файлы открываются для чтения, затем считываются цели­ ком в память и закрываются. После того как необходимые измене­ ния внесены, файл снова открывается, но теперь для вывода, и записывается обратно на диск, перекрывая то, что было записано в его с,екторах и, возможно, захватывая новые сектор.1.
Дисковые накопители 323 Следует отметить несколько моментов; относящихся к этой форме оператора OPEN. Имя файла должно содержать имя нако­ пителя, если файл не найден на накопителе, принятом по умолча­ нию (т.е. накопителе, с котороrо запущен Бейсик). Имя файла может также содержать -Путь к файлу, находящемуся в подката­ логе, например OPEN "l",#1,"A:\LEVELl \LEVEL2\MY- FILE.TXT". Кроме того, вы можете поместить указание размера записи в конце оператора OPEN "R",#3,"MYFILE.TXT",52. В этом случае каждая запись будет занимать 52. байта дискового пространства. Если в операторе FIELDS не будут задействованы все 52 байта, то оставшиеся пропадут. Этот параметр существен при операциях с файлами прямого доступа. Большинство операций с файлами последовательного доступа не требует указания длины записи, однако вы можете ускорить файловые операции, установив размер записи равным 512 байтам. Длина записи может быть в диапазоне от 1 до 32767 байт и по умолчанию равна l 28 байтам. Форма естественного языка. Вторая форма оператора OPEN делает совершr.нно то же самое, что и первая, но использует полные слова. Вместо· того чтобы писать "О" или "I", вы должны писать INPUT или OUTPUT (без кавычек), например, OPEN "FILENAME" FOR INPUT AS #1. Для файлов с прямым доступом не указывается этот параметр: OPEN "MYFILE.TXT" AS #2. Кроме того, вы можете указать режим APPEND, чтобы записать данные, начиная с конца последовательного файла, не уничтожая уже сущест~ующих: OPEN "B:MYFILE.TXT" "FOR APPEND AS #3. Как и для первой формы, в операторе может быть указана необязательная длина записи. Надо просто добавить в конце оператора LEN • число. Например, OPEN "C:MYFILE. ТХТ" AS #1•LEN = 52 открывает файл прямого доступа с записями длиной 52 байта. Часто программа должна запрашивать нужный файл у пользо­ вателя. Для его открытия в операторе OPEN просто подставьте вместо имени файла идентификатор строки, содержащей это имя. При этом необходима проверка на правильность введенного имени. 11* 100 INP!JT "Enter file name: ",F$ 110 IF INSТR(F$,'',.") <> О THEN 130 120 IF LEN(F$) > 8 THEN 500 ELSE 150 ·110дучасм ими файла ·есть ли расширение? ·длиннее 8 символов? ·130 IF LEN(F$) > 12 THEN 500 ·длинвее 12 с~1мволов? 140 IF LEN(F$) - INSТR(F$,".") > 3 THEN 500 ·тип длиннее 3 150 OPEN F$ FOR INPUT AS #1 ·открываем файл
324 Глава 5 500 INPUT "lmproper filename - enter another: ",F$ 510 GQTO 110 • ·если ~•мя 11евер1юе, 1ю111,1й зш1рос Закрытие файла. Закрытие файла тривиально. Чтобы закрыть все открытые файлы, напишите CLOSE. Чтобы закрыть определен­ ный файл или несколько файлов, напишите CLOSE # 1 или CLOSE #1, #3. Важно закрыть все файлы пере.а завершением программы. Если этого не сделать, то в файле могут остаться данные, которые не будут записаны на диск. Отметим, что команды END, NEW, RESET, SYSTEM и RUN закрывают все буфера файлов, но не очищают их. Уже закрытый файл всегда может быть снова открыт с использованием любого доступного буфера файла. Средний уровень MS-DOS обеспечивает различные функции для открытия и закрытия файла, в зависимости от того, использовала ли прог­ рамма для доступа к файлу метод управляющего блока файла или метод дескриптора файла. В обоих случаях могут быть открыты только файлы, которые существовали до этого. Для создания новых файлов применяют специальную функцию (см. [5.3 .2 ]). Метод FCB. Функция 0FH прерывания 21 Н открывает уже существующий. файл. Вы должны сначала создать управляющий блок файла, как показано в [5.3 .5 ]. Перед открытием FCB должен содержать только имя файла и имя накопителя (О = по умол­ чанию, 1 = А и т.д.). DS:DX должны указывать на FCB. Затем надо выполнить функцию. При возврате AL будет содержать О, если файл успешно открыт, и FF, если файл нс найден. В случае, когда для указания накопителя используется О, он будет заменен на код, соответствующий накопителю по умолчанию. Только после того как файл открыт, вы должны установить размер записи (по умолчанию - 128 байт), поля записи прямого доступа и текущей записи (они обсуждаются в разделе, относя­ щемся к операциям с последовательным и прямым доступом). При открытии поле текущего блока заполняется нулем, а поля даты и времени - информацией из каталога. Чтобы закрыть файл с помощью метода FCB, надо vстановить DS:DX на открытый FCB и вызвать функцию lOH прерывания 21Н. При успешном вызове информация о размере файла, дате и времени будет записана в ка_талоr, а в AL будет возвращен О. Однако, если имя файла не будет обнаружено в каталоге или оно будет найдено в другой позиции, изменения на диске будут ин~и­ цированы возвратом FF в AL.
дJсковые накопители ;---в сегменте данных FCB DB l,'FILENAМEEXТ',25 DUP<0) ;---ОТКJ>ЫТИе файла MOV АН,ОFН LEA DX,FCB INT 21Н СМР AL,0 JNE OPEN_ERROR ;---закрытие файла MOV AH,l0H LEA DX,FCB INT 2Щ СМР AL,0 ;номер фу11кции ;DS:DX указыв.1ют на FCB ;открываем файл ;проверка на ·ошибку ;уход на обработку ошибки ;номер функции , ;DS:DX указывают на FCB ;закрываем файл ;проверка щ1 ошибку JNE CLOSE_ERROR ;уход 1ia обработку 011шбк~1 325 Метод дескриптора файла. Для открытия файлов используйте функцию 3DH прерывания 21Н. DS:DX -должны указывать на строку, представляющую путь к .файлу, и ero имя, включая имя накопителя,· если это необходимо. Вся строка должна быть не длиннее 63 байт и завершаться -символом ASCII О. В AL надо поместить код доступа, причем О открывает файл для чтения, l - для записи, а 2 - для чтения/записи. При возврате АХ будет содержать 16-битовый номер файла, по которому файл впослед­ ствии идентифицируется. Файловый указатель устанавливается. на начало файла. Размер записи устанавл.ивается равным I байту - это связано с тем, что операции прямо~;-о доступ~ при использо­ вании метода дескриптора файла не имеют специальных буферов (на самом деле файлы с прямым доступом рассматриваются как последовательные и с ними работают одни и те же функции). Эта функция позволяет открыв&ть как обычные, так и спрятанные файлы. При возврате флаг переноса равен О, .если файл открыт успешно. В противном случае флаг переноса устанавливается, а АХ содержит 2, если файл не найден, 4, если программа хочет открыть слишком много файлов, 6 - при ошибке на диске, и 12, если Q:еправильно указан код доступа в AL. Например: ;---в сегменте данных РАТИ DB ·л:LEVELI\FILENAME.EXT,0 ;---открываем фа~~л для •пе1шя/записи MOV АН.ЗОН ;номер функции
326 MOV AL,2 LEA DX,PATH INT 21Н JC OPEN-_ERROR MOV НANDLE,AX ;открываем для •пеш1я/з.1nнс~1 ;DS:DX указывают на путь ;открываем файл ;уход на обработку ош~1бок ;сохраняем номер файла Глава 5 Функция ЗЕН прерывания 21Н закрывает файл, открытый методом дескриптора файла. Надо просто поместить номер файла в ВХ и выполнить функцию. При возврате флаг переноса Jk1вен О, если все в порядке, иначе он равен 1, а АХ - 6, если указан невер­ ный номер файла. ;---закрытие файла MOV АН,ЗЕН MOV BX,HANDLE INT 21Н JC CLOSE_ERROR ;номер функции ;номер файла ;закрываем файл ;уход на обработку ошибк~• Функция 45Н прерывания 21Н создает второй дескриптор файла из имеющегося -открытого. В ВХ должен быть указан существу~щий номер, а в АХ будет возвращен новый. Функция 46Н прерывания 21Н связывает второй дескриптор (помещаемый в СХ) с открытым файлом (номер кo:roporo в ВХ> таким образом, что первый будет относиться к тому же файлу и устройству, что и последний. • 5.3 .4 . ~ереименование файnа; изменение позиции файnа в катаnоrе Переименование файла может за~ючаться лишь в изменении первьiх 11 символов элемента каталога. Однако в древовидном каталоге весь элемент каталога может быть перенесен в другой подкаталог, переопределяя тем самым путь к файлу. Достаточно одной команды, чтобы переименовать файл или •перенести его в другой каталог. Высокий уровень В Бейсике файл переименовывается командой NAME. С помощью этой команды он может быть также перенесен в другой каталог. Напишите сначала существующее имя, а затем новое имя файла, оба заключенные в кавычки, например NAME
Дисковые накопители 327 "OШFILE.EXT" AS "NEWFILE.EXT". В этом случае будет пере­ именован файл в корневом каталоге. Для изменения имен файлов, расположенных в подкаталогах, могут быть использованы пути к файлу. Например, NAME "B:LEVELI \OLDFILE.EXT" AS "B:LEVELI \NEWFILE.EXT" изменяет имя файла в подкаталоге LEVELI. Отметим, что для нового имени файла должен быть указан полный путь. Если вы запишете NAME "B:LEVELl \ OLD- FILE.EXT" AS "NEWFILE.EXT", то файл будет не только пере­ именован, но и перенесен в корневой каталог. Для переноса ф.1йла из одного подкаталога в другой без изменения его имени введите команду NAME "A:SUBDIRI \OLDFILE.EXT" AS "A:SUB- DIR2\OLDFILE.EXT". Таким методом нельзя перенести файл с диска на диск. Поскольку файлы, расположенные в разных ката­ логах, могут иметь однQ и то же имя, то возможна ошибка при попытке переноса файлов с одинаковыми име'нами. В этом случае будет возвращен код ошибки 58 (см. [5.4.8 ]) . Средний уровень MS-DOS может переименовывать файлы, используя как метод управляющего блока файла, так и метод дескриптора файла. Первый из них может применяться только к файлам, располо­ женным в текущем каталоге. Метод FCB. Примените функцию 17Н прерывания 21Н. DS:DX должны указывать на открытый управляющий блок файла. Помес­ тите •новое имя файла в FCB, начиная со смещения 11 Н (это "резервная" область блока). Новое имя может использовать символ "?", в этом случае символы, находящиеся в этих позициях, не будут изменяться. При возврате, если новое имя уже существо­ вало в каталоге, AL будет равно FF, иначе - О. В следующем при­ мере имя файла ACCOUNTS.DAT меняется на DEBTS.DAT. ;---в сеП11енте данных FCB D8 'FILENAMEEXT,25 DUP(O) NEWNAМ D8 'NEWNAME ЕХТ, ; 11 символов нового имени ;---помещаем новое имя файла .в переменную NEWNAM REP MOV Sl,OFFSET NEWNAМ ;DS:SI указыв.1ют на новое имя MOV AX,SEG FCB ;ES:DI указыв.'lют на FCB MOV ES,AX MOV Dl,OFFSET FCB ADD DI,IIH MOV СХ,1I MOVSB LEA DX,FCB ;на 1 ш11аем со смеще1шя 111-1 ;имя файла содерж•~т 11 символов ;переносим 11 байт ;DS:DX указывают на FСП
328 MOV АН,17Н INT 21Н СМР AL,0FFH JE RENAME_ERROR Глава 5 ;фу11к1н1я изме11с111111 ~•мсш1 ;~1змс11яем ими ;r1ро11ерка 1щ 01ш1бку ;уход на обра6отку оншбки Метод дескриптора файла. Функция 56Н прерывания 21 Н переименовывает и перемещает файлы. DS:DX должны указыват1, на строку, показывающую путь и имя переименуемоrо файла (до 63 символов) и завершающуюся символом ASCII О. ES:DI должны указывать н~ вторую строку, которая дает новые имя и путь файла. Имена накопителей, если они присутствуют, должны совпадать. Если пути различны, то файл переносится в другой подкаталог. Чтобы перенести файл без переименования, надо во второй строке указать то же самое имя, НО другой путь. При возв­ рате, если произошла ошибка, устанавливается флаг переноса, а АХ будет содержать 3, если один из путей нс найден, 5 - при ошибке на диске- и l 7 - при попытке переноса между разными накопителями. В предлагаемом примере файл ACCOUNTS.DAT переносится из подкаталога GAINS в подкаталог LOSSES: ;--·-в сегменте данных OLDPATH DB 'A:GAINS\ACCOUNTS.DAT ,0 NЕWРАТН DB 'A:L.OSSES\ACCOUNTS.DAT ,0 ;---изменение пути файла LEA DX,OLDPATH ;DS:DX указ1,шают 11а старый пуп, MOV AX,SEG NEWPATH ;ES:DI указ1,111ают 1ш 1ю111,1й 11уть MOV ES,AX MOV DI,OFFSET NEWPATH MOV АН,56Н ;номер фу11щш1 INT 21Н ;nере,юсим файл JC ERROR ROUТINE ;уход на обрnботку ошибк~1 5.3.5 . Подrотовка к файnовым операциям Языки высокого уровня, такие, как Бейсик, в1,1полняют подго­ товительную работу для файловых операций автом.1тически. Однако программы на языке ассемблера имеют достаточно работы, перед тем как создать и.1и открыть файл. Требования отличаются в зависимости от того, используется ли для доступа к файлу метод управляющего блока файла или метод дескриптора файла. Для обоих методов вам необходимо подготовить строку или блок параметров, указывающих на файл и буфер для переноса данных.
Дисковые накопители 329 MS-DOS предоставляет различные наборы функций чтения/ за­ писи для обоих методов. Средний уровень Метод управляющего блока файла. Этот метод доступа к фай­ лам требует, чтобы вы создали блок параметров, который первона­ чально должен содержать такую информацию, которая позволяет найти файл в каталоге. Хотя FCB имеет много полей, но только некоторые из них должны быть заполнены; остальные же MS-DOS заполняет информацией после того, как файл открывается. Отме­ тим, что к началу FCB может добавляться специальное поле для создания расширенного FCB, который объясняется ниже. Вот • структура FCB: Накопитель (DB) Число, определяющее, на каком накопителе бу­ детискатьсяфайл,l=А,2=Вит.д.Если указан О, то берется накопитель по умолчанию, а затем система заменяет О на код этого нако- пителя. Имя и расширение Восьмибайтовое имя файла, выравненное поле- O1 байт) вому краю, должно. быть дополнено пробелами (ASCII 32), если оно меньше 8 байт. То же от­ носится и к трехбайтовому расширению. Между Текущий блок (DW) Размер записи (DW) ними не должна стоять точка. DOS организует файлы блоками по 128 запи­ сей, пронумерованных от О до 127. Например, система рассматривает запись # 129 файла пря- мого доступа как запись. #О блока # l (отсчет как для записей, так и для блоков ведется с 0). В файлах нет специальных ограничителей ни для блоков, ни для записей. Вместо этого смещение для блоков и записей вычисляется исходя из длины записи, которая устанавлива- ется следующим полем FCB. Все функции MS-DOS, связанные с чтением или записью в файл, работают в терминах за­ писи. Для файлов прямого доступа важно, что- бы размер записи был установлен равным раз­ меру записей, ·помещенных в файл. Для после­ довательных файлов размер записи не столь ва­ жен, однако маленький размер записи будет за­ медлять дисковые операции. Поскольку размер сектора равен 512 байтам, то оптимальным раз­ мером записи будет эта же величина. Система
330 Размер файла (DD) Дата файла (DW) Текущая запись (DB) Глава 5 автоматически помещает значение по умолча­ нию 80Н О 28) в поле длины записи при откры­ тии файла. Поэтому не забудьте установить это nоле после открытия файла. Размер указывается с точностью до байта. Это поле заполня~тся систсчоii при открытии файла. Дата записывается системой при открытии FCB. Ее формат приведен в (5.2.5 ]. Текущая запись используется совместно с по­ лем текущего блока. Записи нумеруются от О до 127. Запись прямоrо доступа #200, располо­ женная в_ блоке 1, имеет номер текущей запи­ си, равный 71 ((200 - 128) - 1). Номер записи пря- Вместо тоrо чтобы требовать от программы вы­ мого доступа (DD) числения текущеrо значения блока и записи для файла прямоrо доступа, MS-DOS делает эту работу сама. При операциях с файлами прямого доступа просто поместите номер записи в это четырехбайтовое поле. При выполнении опера­ ции с файлом прямого доступа MS- DOS помес­ тит нужные значения в поля текущего блока и текущей записи. Помните, что старший байт расположен в старшей ячейке. Связь между полями текущей записи, текущего блока и номера записи прямого доступа показана на рис. 5.3. Текущмй ТекуЩ811 Номерэапмси блок защ,сь ЛJ)JIMOГO доступа о о о о· 1 1 о 2 2 . . . . . . . . . . . . . . . '7 7 1 о 128 1 1 129 1 2 130 . . . . . . . . . . . . . . . Рис. 5.3 . Организация записей прямого доступа в методе FCB
Дисковые накопители 331 Простейший путь - создать FCB как переменную в сегменте данных программы. Если имя открываемого файла не меняется, то оно может быть непосредственно записано в это поле. Остаток блока инициализируйте байтами ASCII О. Только после того как FCB будет открыт (с помощ~ю функции OFH прерывания 21 Н, как показано в [5.3 .3 ]) , вы должны записать в блок остальную инфор­ мацию. Отметим, что FCB для работы с простым последова­ тельным файлом с длиной записи, равной 128 байтам, не требует дальнейших приготовлений. После создания FCB при последующих операциях нужно, чтобы DS:DX указывали на него. Простейшая его форма такова: FCB DB l ,'FILENAMEEXТ' ,25 DljP (О) Можно также создать FCB в виде структуры: FCB SТRUC DRIVE NUM DB О FILE NAME DB 8 DUP(?) FILE ЕХТ DB 3 DUP(?) BLOCK_ NUM QW О RECORD_SIZE DW О FILE_SIZE FILE_DATE RESERVED DDО DWО DB 10 DUP(O) CURRENT REC DB О RANDOM REC DD О FCB - ENDS При таком подходе программе проще помещать данные в FCB, поскольку метки существуют для каждого поля. В зависимости от типа файловых операций на полsr могут накладываться следующие ограничения. l. Для файлов прямого доступа вы должны установить размер и номер записи в поле записи прямого доступа. 2. Для доступа к началу последовательного файла необходимо установить только размер записи при условии, что вы инициализировали поля текущего блока и текущей записи в О (просто обнулите весь FCB, за исключением имен накопителя и Файла). При открытии поле размера записи будет установлено равным 128, если это значен11е вас устраивает, дальнейшая подготовка не нужна.
332 Глава 5 3. Для доступа к середине или к концу последовательного файла нужно установить поля текущего блока и текущей записи (в этом случае ваша программа должна будет производить вычис­ ления сама-). Префикс программного сегмента (см. ( 1.3.О )> имеет достаточно большое поле, чтобы содержать управляющий блок файла. Это пространство, предоставляется для каждой пррграммы, поэтому рекомендуетСS! его использование для экономии памяти, особенно в программах типа .СОМ.· Поле FCB расположено со смещением SCH в префиксе программного сегмента. В программах СОМ используйте ORG для создания FCB следующим образом (здесь помечен ·также принятый по умолчанию DTA, который будет обсуждаться J{иже):1 ;---в начале кодового сегмента ORG 5СН FCB LABEL ВУТЕ DRIVE NUM DB о FILE NAME DB 8 DUP<?> FILE ЕХТ DB 3 DUP(?) BLOCK_NUM DW о RECORD_SIZE DW о FILE_SIZE DD о FILE DATE DW о RESERVED DB 10 DUP<O> CURRENT_REC DB о RANDOM _R~C DD о ORG 80Н DTA LABEL ВУТ1': ORG !ООН ASSUME. CS:CSEG, DS:DSEG, SS:SSEG Расширенный FCB необходим для создания или доступа к файлу, имеющему специальные атрибуты, например к спрятан­ ному файлу или файлу только для чтения. Различные атрибуты объяснены в [5.2.6 ). Расширенный FCB на 7 байт длиннее, пр~1чем эти 7 байт предшествуют обычному блоку. Первый байт равен FF, что указывает на специальный статус. За ним следуют 5 байт ASCII О, а затем - ·байт атрибутов. При открытии файла с помощью расширенного FCB DS:DX должны указывать на первый из дополнительных семи байтов, а не на имя накопителя, как для
Дисковые накопители 333 обычного FCB. Вот_ общая форма, где 2 - значение байта атри­ бутов, а 1 - указывает на накопитель: FCB DB OFFH, 5 DUP(0),2,l,HLENAMEEXГ,25 DUP<O) Метод дескриптора файла. Этот метод требует меньшей подго­ товки, чем метод FCB; Для него вы должны только создать строку, указывающую путь к файлу, такую, как в стандартных командах DOS. Например, B:COMPILE\ UТILIТY\PASCAL указывает на файл PASCAL в подкаталоге UТILIТY. Строка ограничена длиной в 63 символа, включая имя накопителя. При открытии файла (с использованием функции ЗDН прерывания 21 Н - см. [5.3 .3 )), DS:DX должны указывать на первый байт этой строки. Система выполняет всю работу по анализу строки и нахdждению файла, а после того как файл открыт, она возвращает 16-битовый иденти­ фикационный номер файла в АХ. Его называют номероr., файла, и он используется во всех последующих операщ~ях с этим файлом. Буфера данных. Программа должна указать место в памяти, куда помещаются принимаемые данные или откуда должны браться выводимые. Это пространство в памяти может быть вре­ менным буфером, который будет использоваться данными как промежуточная станция. Или это пространство может 1быть именно тем местом, где данные реально обрабатываются. Обычно времен­ ный буфер устанавливается размером в одну запись и бывает удобно описать его как строковую переменную в сегменте данных, что и сделано в приведенном ниже примере. С другой стороны, большие, рабочие области данных должны распределяп,ся с помо­ щью методов распределения памяти, предоставляемых операци-' онной системой (см. [ 1.3.1 )) . Ведь создание, например, области данных размером в 10000 байт в сегменте данных сделает прог­ рамму на диске на 10000 байт длиннее, что совершенно не нужно. Буфер, используемый методом FCB доступа к файлам, называ­ ется областью обмена с диском или DT А. Имеется .словный указа­ тель на этот буфер, который хранится операционной системой и может быть изменен ващей программой. В фирменной докумсн­ таци1;f. сам указатель на DTA часто назывзют DTA. Поскольку указано только начало буфера, то ничто не мешает данным занять область, прилегающую 15 DTA, поэтому вы с~lми должны следить, чтобы этого нс произошло. Указатель на DTA устанавливается специальной функцией OOS и после того, как он установлен, все функции чтения/записи автоматически обращаются к нему. Это означает, что сами функции не должны содержать адрес времен­ ного буфера.
334 Глава 5 Когда DTA совпадает с областью данных, в которой они обра­ батываются, необходимо постоянно менять DT А, с тем чтобы фай­ ловые операции могли получать доступ к различным фрагментам данных. При простой операции последоватет,ноrо чтения или при операции чтения одного блока с прямым доступом система автома­ тически помещает в DTА одну запись за другой. Необходимо отвести пространство, достаточное для числа записей, которые будут затребованы программой. DTА не может иметь размеры больше одного сегмента (64К). Для установки указателя на DTA используйте функцию IAH прерывания 21Н. DS:DX должны указывать на первый байт DTA. Затем надо выполнить функцию. Это все, что вам нужно. К при­ меру: ;---в сегменте данных DTA DB 256 DUP (?) ;---установка DTA LEA DX,DTA MOV АН,IЛН INT 21Н ;DS:DX указ1,111ают щ1 DТЛ ;фу11кцш1 установки DТЛ ;уста11011ка DТЛ Функция 2FH прерывания 21Н сообщает текущую установку указателя DTA. У нее нет входных регистров. При возврате ES:BX содержат сегмент и смещение DTА. Префикс программного сегмента (см. [l.3 .0 ]) обеспечивает каждую программу 128-байтовым встроенным DTA, начиная со смещения 80Н и до 9FH. Вы можете применят,, его при нехватке памяти. Первоначально указатель 'на DТ А установлен именно на этот буфер, поэтому, если вы будете использовать его, нет нужды устанавливать указатель. Этот буфер по умолчанию особенно удобен при работе с СОМ-файлами, где DS указывает на начало префикса программного сегмента. Для файлов ЕХЕ может потре­ боваться небольшой добавочный код, чтобы использовать DT А по умолчанию. Отметим, что определить текущую установку указа­ теля на DTA вам может помочь функция 2FH прерывания 21Н. У нее нет входных регистров, а при выходе ES:BX указывают на DTA. Указатель на DTA не используется при доступе к файлу мето­ дом дескриптора файла. Функции чтения J:fJIИ записи данных всегда содержат адрес, по которому расположен буфер данных. Только от вашего желания зависит, будут ли данные передаваться через временный буфер или непосредственно в то место, где, они будут обрабатываться.
Дис,:овые накопители . 335 5.3.6 . дналнз ннформацнн командной строкн При запуске. многие проrр.1ммы дают пользователю возмож­ ность поместить добавочную информацию в командной строке, обычно указывающую имя файла, с которым программа будет работать. Эта информация записывается в 128-байтовую область, начинающуюся со смещения 80Н в префиксе программного сег­ мента [1.3.0 ]. (Эта же область используется как D,T А по умолча­ нию, см. в (5.3 .5 ].) Первый байт содержит длину строки, а за ним следует собственно строка. Для работы с файлами методом дескриптора файла имя файла, вводимое в командной строке, должно иметь адекватную форму. Требуется, чтобы пользователь программы применял стандартный протокол MS-DOS для строки пути. С другой стороны, упр.1вля­ ющий блок файла требует, чтобы строка вида ·л:АССТ.ВАК' была преобразована к виду 1,'АССТ влк·. MS-DOS имеет специальную функцию, которая выполняет такое преобразование над первой порцией информации, следующей за именем программы в коман­ дной строке. Эта процедура называется разбором строки (parsing). / Средний уровень Имя файла-это первая информация, следующая за именем загружаемой программы. Оно должно быть отделено от имени программы одним из следующих символов : . ; , = + табуляцией или пробелом. Конец имени файла должен быть обозначен одним изсимволов:.; , = .+ \ <> 1/"[]табуляцией,пробелом или одним из управляющих символов (коды ASCII от I до 31 ). Функция 29Н прерывания 21 Н производит разбор имени файла. DS:SI должны указывать на смещение 81 Н в PSP. Помните, что при загрузке программы как DS, так и ES установ­ лены на начало PSP. ES:DI должны vказывать на область памяти, которая будет служить управляющи·м блоком для нового файла. Установ ка битов в AL определяет, как· будет выполняться разборка. Име~ значение только биты 0-3: бит о 1 = начальный ограш1•1итсль игнорируется 1 = байт, идентифицирующиr1 накопитель, устанамиа1ается 11 FCB, только если он указ.,н в командной строке 2 1 = имя файла в FCB меняется, только сели кома~щная строка содержит имя файла 3 1 = расширение файла в FCB меняется, только если командная строка содержит расшире1ше файла
336 Глава 5 После того как эта информация установлена, программ~t может вызывать функцию. Если в командной строке нс указан накопи­ тель, то берется накопитель по умолчанию. Если отсутствует рас­ ширение файла, то предполагается, что оно состоит из пробелов (ASCII 32). Если в имени файла указана звездочка, то она заменя­ ется на нужное число вопросительных знаков в поле имени файла FCB. AL возвращает 1, если имя файла содержит * или '?, и FF, если указан неверный накопитель. При возврате DS:SI указывают на первый символ, следующий за именем файла, которое начинается со смещения 81 Н. Даль­ нейшая информация, содержащаяся в командной строке, должна расшифровываться программой. ES:DI указывают на первый б.1йт вновь сформиwванноrо FCB. Если в FCB не создано допустимого имени файла, то содержимое ES: [DI] + 1 равно пробелу. В предла­ гаемом примере код помещается в область FCB в PSP, начиная со смещения SCH: ;---разбираем командную строку, создавая FCB со смеще11нсм SCI_I ;---в PSP 1,10v AH,291-I ;11омер функфш моv Sl,8IН ;DS:SI указыва~о·r щ1 ~•мя фаr1Ла моv Dl,SCH ;ES:DI указмпают 11а обласп, l'CB MOV AL,1111B ;кодовый байт INT 21Н ;установка FCB моv AL,ES: [DI] + 1 ;полу•Iение статусной информа~tш1 СМР AL,32 ;был лн установлеII файл JE ERROR_ROUТINE ;если нет, то уход на обработку 01ш1бк~1 5.4. Чтение и запись файла Имеются два основных метода достvпа к файлу - последовательный и прямой. Хотя в литературе по ~ычис­ лительной технике часто испол1,зуют термины ·•последовател~,ный" файл и файл "прямого доступа", с.1ми по себе файлы хранятся на диске одинаково: как непрерывная последовательность байтов. Ни в каталоге, ни в каком-либо другом месте нет индикатора, указы­ вающего, что данный файл является последовательным или файлом прямого доступа. Реаль_но эти два типа файлов различа­ ются по расположению данных в них и по методу доступа к ним. К любому файлу прямого доступа можно получить последова-
Дисковые накопители 337 тельный доступ, а к любому последовательному файлу - прямой доступ, хотя причины делать это появляются редко, особенно во втором случае. Последовательные файлы помещают элементы данных один за другим, независимо от их длины, разделяя эти элементы парой символов: сначала возвратом каретки (ASCII 13), а затем пере­ водом строки (ASCII 10). Языки высокого уровня, такие, как Бейсик, вставляют эти символы автоматически, в то время как программы на ассемблере должны сами заботиться о вставке этих символов после записи каждой переменной в файл. В последова­ тельных файлах могут храниться как числа, так и строки. Строки требуют по одному байту на каждый символ строки. Числа по соглашению записываются в строковом виде, хотя есть возмож­ ность их записи и в числовом виде. Таким образом Бейсик записы­ вает значение "128" в виде строки из трех цифр, хотя программа на ассемблере может заn_исать это •число в виде двухбайтовою целого или даже однобайтового кода - все определяется тем, что при повторном чтении файла программа должна понимать исполь­ зуемый формат. Для совместимости рекомендуется записывать числа в виде.строк. Необязательно, чтобы каждое число строки было отделено парой возврат каретки/ перевод строки, однако если ::rra пара опу­ щена, то программа должна обеспечить способ отделения данных. Например, 10 целых чисел могут быть записаны как 20-байтовый элемент данных. С другой стороны, очень большие элементы данных, такие, как параграфы текста, могут быть разделены на несколько элементов данных (стандартный текстовый ~1йл пред­ ставляет из себя документ, разбитый на строки удобноrо размера, записанные последовательно). Поскольку элементы данных имеют переменную длину, то невозможно узнать, где в файле расположен определенный элемент. Поэтому для того чтобы найти нужный элемент, программа должна читать файл, начиная сначала и отсчитывая нужное число пар возврат каретки/ перевод строки. По этой причине файлы такого формата называют последователь­ ными. Как правило, с диска в память передается весь такой ~1йл. Файлы прямого доступа заранее отводят фиксированное место под каждый элемент данных. Если какой-то элемент данных не занимает все отведенное пространство, то остаток заполняется про­ бедами. Если кажды;й элемент занимает 10 байт, то легко можно просмотреть сразу 50-й элемент, поскольку можно вычислить, что он начинается с 491-ro байта файла (т.е. с байта #490, поскольку отсчет начинается с О). Как правило, связанный набор элементов группируется в запись. Каждая запись содержит несколько полей, которые создают набор номеров байтов, начиная с которых пишутся данные элементы. Например, запись может имста. поля:
338 Глава 5 возраст, вес и рост. Соответственно :JТи по,111 могут занимат,, 2. З и 5 байт, а вместе образовывать зап1ю, дл11ноii в 10 баiiт. Фай:; прямого доступа может состоять из тысяч т;Iк11х зап11сей. К:1ж.ы11 запись следует непосредственно за прсдшествующ6i без всяких ограничителей, таких, как пары возврат каретки/ лсрсво;~ строки, используемые в последовательных файлах. При :)Том записи можно делать в любом порядке и можно вывести запись 74, хотя запись 73 еще не была выведена (при этом д,1я записи 73 отведено дисковое пространство, и она будет содержат,, те д;тные, кото;ч,I1: случайно оказались в том секторе, в которсн,1 от1,с,1сно r,1есто .,.1s1 этой записи). В отличие от пос..~с..юва1.:.1I,ных файл:,1 11ря:,,uго доступа остаются на диске. В памяти присутствуют то,1I,ко определенные записи, с которым11 11дет работа в ;:~анный момент времени. Когда для прямого доступа к файлу испот,зустся управляющий блок файла, системе сообщается размер зап11с11 фай,1а <вес записи данного файла должны иметь одинаковую длину), Это позволяет программе запросить любую запись по номеру, а MS-DOS точно вычислит, где эта запись расположена на диске. При работе с фай­ лами прямого доступа методом дескриптора ф;~й,1а программа должна сама вычислять положение требуемой записи. Система хранит файловый указатель для каждого буфера файла. Он указывает на п-й б:~йт файла, опреде,1яя место в файле, с которого будет начинаты:sI следующая операция чтения или записи. При последоватсл~,ной операции псрсзаписи файловый указатель первоначально устанавливается на начало файла и постоянно сдвигается по мере того, как вес новые и новI,Iе даннI,Iе записываются в файл. Когда данные ;:~обавляются к последователь­ ному файлу, то файловый указатслI, псрвоначалI,но устанав"111ва­ ется на конец файла. При обращении к одной записи в файле пря­ мого доступа положение записи вычисляется в в11дс смещения относительно начала файла и указатель устанавливается равным этому значению; затем нужная запись читается или пишется. Обычно за файловым указателем следит система, однако прог­ рамма может сама управлять им и манипулировап, для своих специальных нужд. Единственным примером низкого уровня в данном разделе является чтение/запись одного сектора. Чтение или зап11сь цслых файлов состоит в последовательности таких чтсниii или записей одного с~ктора, программируя микросхему контроллер;~ НГМД заново для каждого сектора. Полномасштабные файловые опера­ ции очень сложны на этом уровне, что с.,1едует хотя бы из боль­ ших размеров файла COMMAND.COM. Однако, изучив обсуж­ дение операций низкого уровня, а также имея информацию о таб-
Дисковы-е накопители 339 лице размещения файлов (5.1.1] и дисковых каталогах (5.2 .1 ], вы можете представить, как работает дисковая операционная система. 5.4.1 . Проrраммнрованне контроллера НГМД 765 н мнкросхемь1 прямоrо доступа к памятн 8137 Микросхема контроллера НГМД 765 фирмы NEC управляет мотором и головками накопителя на дискетах и обрабатывает потоки данных, направляемые в или из дисковых секторов. Один контроллер, установленный на плате адаптера дисков, может обслуживать до четырех НГМД. За исключением случаев, свя­ занных с защитой от копирования, программировать микросхему контроллера НГМД прямо не приходится. Процедуры работы с дисками, предоставляемые DOS и ВIOS, эффективны и удобны, и, кроме того, очень рискованно писать свои собственные процедуры, поскольку ошибки в них могут разрушить дисковый каталог или таблицу размещения файлов, что вызовет полное разрушение информации на диске. Предлагаемое в этом параграфе обсуждение вопросов ставит перед •собой цель дать вам только общее представление о них. Листинг ROM-BIOS, приведенный в конце каждого технического руководства по MS-DOS, содержит код тщательно разработанных процедур для форматирования дискет, чтения и записи секторов, а также сброса и получения статуса накопителей. После того как вы усвоите приведенный здесь материал, изучите процедуры ROM- B10S для продолжения вашего образования в области операций с дисками на низком уровне. Вам потребуется также документацщ1 по микросхеме контроллера НГМД 8272А фирмы Intel, которая аналогична микросхеме фирмы NEC. В данной документации перечислены прерывания, генерируемые контроллером НГМД, в то время как в документации по IВМ РС этого списка нет. Инфор- • мация о микросхеме 8272А может быть найдена во втором томе справочника по компонею·ам микросистем (Microsystem Compo- nents HandЬook, Volume 2). Контроллер НГМД может выполнять 15 операций, из которых здесь· будут об~уждаться только три: поиск, чтение или запись одного сектора. Понимание работы этих операций позволит вам выполнить любую из оставшихся двенадцати при условии, что у вас будет упомянутая выше информация. Чтение файла состоит в поиске его в к~талоге (5.2. l ], определении его положения на диске с помощью таблицы размещения файлов (5.1.l] и затем· наборе операций чтения одного сектора. Эта процедура включает 6 шагов. 1. Включение мотора и короткое ожидание, пока он наберет обороты.
340 Глава 5 2. Выполнение операции поиска и ожидание прерывания, ука­ зывающего на завершение этой операции. 3. Инициализация микросхемы ОМА для пересьiлки данных в память. 4. Посылка команды чтения конт-J?Оллеру НГМД и ожидание прерывания, указывающего, что пересылка данных завершена. 5. Получение информации о статусе контроллера НГМД. б. Выключение мотора. Контроллер НГМД работает через три порта ввода/вывода (на самом деле микросхема имеет больше чем три регистра, но доступ к большинству из них осуществляется через один порт): ЗF2Н ЗF4Н ЗFSH реr~1стр цифро110rо п1>11101щ регистр статуса регистр да11111,1х Первый шаг состоит в доступе к регистру цифрового вывода. Значение его битов следующее: биты- 1-0 выбор накопителя, где 00А 01в 10с 11 D 2 О сброс контроллера НГМД 3 разрешение прерьшання FDC и достуг,а DМЛ 7-4 включение мотора нако11ителя D-Л (бит 4 = Л) Это регистр только для записи, поэтому необходимо заботитьсs.1 обо всех его битах. В приведенном ниже примере используется накопитель А, поэтому цепочка битов должна имет1, вид: 00011100. Такая установка битов выбирает накопитель А, сохраняет установленным бит 2, разрешающий работу с НГМД, и включает мотор накопителя А. Не сбрасывайте бит 2 в нуль, так как в этом случае вам придется производить перскалибровку накопителя - действие, которое очень редко бывает необходимо. • "Перекалибровка" . накопителя подразумевает возврат его головки на нулевую дорожку. Эта операция осуществляется пос1,1,1- кой простой последовательности команд контроллеру НГМД. Конт­ роллер НГМД управляет текУ,щей позицией головки посредством запоминания всех изменений позиции головки после се начал1,ной установки на нулевую дорожку. Когда контроллер НГМД сбрасы­ вается за счет изменения бита 2 регистра цифрового вывода·, то значение текущей l(Юзиции головки устанавливается в нуль, нсза-
Дисковые накопители 341 висимо от того, на какой дорожке находится головка на самом деле, что делает необходимым перекалибровку. Обычно сброс контроллера НГМД производится только в случае такой серьезной ошибки накопителя, после которой неизвестно текущее состояние контроллера НГМД и накопителя. Отметим, что выбор накопителя и включение его мотора - отдельные действия. Контроллер НГМД может иметь доступ только к одному накопителю в данный момент времени, но моторы могут быть включены у нескольких. Моторы могут оставаться включенными еще несколько секунд после завершения обмена данными в .ожидании следующего доступа к накопителю. Такая стратегия позволяет избежать потери времени на по.вторное ожидание, пока мотор наберет скорость. Напротив, мотор нельзя оставлять постоянно включенным, так как это приведет к преждевременному износу дискет. Работа микросхемы контроллера НГМД делится на три фазы: командную, выполнения и результата. В командной фазе один или более байтов посылаются в регистр данных. Последовательность байтов строго фиксирована и меняется от команды к команде. Затем контроллер НГМД выполняет команду, . и в :по время он находится в фазе выполнения_. Наконец, во вре.мя фазы резул1,тата ряд байтов статуса счи1,ъ1ваются из регистра данных.1 При этом необходимо, чтобы не было ошибки в числе передаваемых или считываемых данных в. регистр данных в фазах командной и результата. Число байтов команды и результата меняется в зависимости от выполняем6й контроллером дисковой операции. В техническом руководстве по IВМ РС приведены данные для всех 15 операций. Первый байт команды является кодом, определяющим требуемую операцию. Номер кода содержится в младших пяти битах байта, в некоторых случаях в старших трех битах закодирована добавочная информация. Часто второй байт команды содержит номер накоhи­ теля (0-3) в младших двух битах и номер головки (0 или 1) в бите 2, все остальные биты контроллером НГМД игнорируются. При операции поиска требуется дополнительно еще только один байт, в котором должен содержаться номер новой дорожки. Для чтения или записи сектора требуется семь дополнительных командных байтов, которые идентичны в этих двух случаях. Байты с третьего по пятый сtщержат текущий номер дорожки, номер головки и номер сектора. За ними следуют четыре байта, содержащие техни­ ческую информацию, необходимую для контроллера НГМД. Первый байт этой технической информации относится к числу байтов в секторе, которое кодируется как О для 128, l для 256, 2 для 512 и 3 для 1024. Конечно, дискеты, созданные в MS-DOS, имеют сектора размером 512 байт. Затем идут · .:~анные конца
342 Глава 5 дорожки . (ЕОТ), которые дают максимальный номер сектора для цилиндра; это значение равно 9 для дискет емкостью 36ОК. Нако­ нец, следует байт, представляющий длину сдвига (GPL, равный 2АН) и длину данных (DTL, равный FFH). Техническое руко­ водство по IВМ РС содержит таблицу, где объясняются другие входные параметры, например те, которые· используются при форматировании диска. MS-DOS хранит четыре технических пара­ метра в памяти, в специальной таблице параметров, называемой базой диска (disk base). Вектор прерывания l ЕН указывает на эту таблицу. Значения параметров хранятся в том порядке, в котором они должны быть переданы контроллеру НГМД, начиная со сме­ щения 3. В следующей таблице показана командная последова­ тельность для трех операций, используемых в приведенном ниже примере. 1J цепочках битов черех Х обозначены биты, значение которых несущественно, через Н - номер головки, а через DD - номер накопителя. Операция # байта Функция Установка для головки О дорож- ки 15, сектора 1 Поиск 1 номер кода IFH 00001111 2 головка и накопитель оон XXXXXHDD Чтение 1 номер кода01100110 66Н сектора 2 головка и накопитель оон XXXXXHDD 3 номер дорожки 0FH 4 номер головки оон 5 номер сектора ОIН 6 байтов в секторе 92н 7 конец дорожки 09Н 8 длина сдвига !АН 9 длина данных FFH Запись 1 номер кода 01000101 45Н сектора 2-9 теже,чтоидля чтения сектора Вы должны быть уверены, что контроллер НГМД готов, пре~е чем вы пошлете или прочитаете байт из регистра данных. ,
Дисковые накопители 343 Биты 7 и 6 регистра . статуса предоставляют :лу информацию. Ниже перечислены значения битов :лоrо регистра: бim,1 3-0 4 s 6 7 ! 1 о накопитель D-A в режиме поиска контроллер НГМД выполняет команду 11те11ия/заш1с~1 контро.1лср НГМД 11е в реж11мс DМЛ регистр данных контроллер., НГМД готов к приему да11ных готов к посылке данных контроллер НГМД rотов к посылке или приему данных Перед началом операций с диском неплохо проверить, равен ли бит 6 нулю, индицируя, что контроллер НГМД ожидает команду. Если он ожидает посылки данных, то произошла ошибка. Когда байт данных посылается в регистр данных, то бит 7 регистра ста­ туса становится равным нулю; продолжайте чтение регистра до тех пор, пока бит не изменится обратно на 1, а затем посылайте следующий байт команды. Аналогично проверяйте бит статуса перед чтением байта статуса в фазе результата. В конце приведен­ ного ни~е примера показаны две процедуры, которые выполняют эти функции. Когда операция поиска завершена, то контроллер НГМД ини­ циирует прерывание 6, прерывание от НГМД. Хотя так же просто можно узнать об окончании операции поиска, проверяя регистр статуса, в примере это делается за счет обработки прерывания. Когда происходит прерывание, то обработчик прерывания BIOS устанавливает бит 7 байта статуса поиска в области данных BIOS, расположенного по адресу 004Q:ООЗЕ. Это единственный результат обработки прерывания. Можно проверять этот байт до тех пор, пока бит 7 не будет установлен, а затем переходить к следующему шагу операции чтения сектора. Следующий шаг состоит в инициализации микросхемы прямого доступа к памяти 8237. Она предназначена для обмена данными между периферийными устройствами и памятью, т.е. для работы, которой может заниматься также процессор. На самом деле в PCjr, где нет микросхемы DMA, контроллер НГМД посылает данные прямо в процессор, который в свою очередь пересыла~т их в память. Тактовая частота процессора адекватна этой задаче, однако при пересылке данных все прерывания должны быть запре­ щены, с тем чтобы не происходило потери данных. Это означает, что в PCjr при передаче данных ввод с клавиатуры или из модема запрещен. Прерывания таймера также игнорируются, однако впос-
344 г,,ана 5 ледствии счетчик времени суток обновляется спсщ1а-11,ной проце­ дурой, использующей канал 1 микросхемы таймера 8253 для под­ счета импульсов, прошедших за время дисковых операций. Все остальные модели IBM РС имеют микросхему ОМА, по:лому про­ цессор свободен при передаче данных. В IВМ РС и ХТ применяется четырехканальная микросхема DMA 8237. Канал О предназначен для "освежения" ш1мят11 (memory refresh); он постоянно восстанавливает заряд ячеек опе­ ративной памяти. Если вы будете работать по этому каналу, то :>то приведет скорее всеrо к поломке машины. Канал 2 предназначен, для дисковых операций, а два других канала, с номерами 1 и 3, доступны (через разъемы расширения) для дополните,,1,ного обору­ дования. К сожалению, обмен память-память требует двух каналов и одним из них должен быть канал О, поэтому такой обмен недос­ тупен на IВМ РС и ХТ. Однако АТ имеет 7 каналов прямого доступа к памяти и DMA автоматически используется инструк­ циями MOVS, существенно увеличивая производителr,ност1,. Перед инициализацией канала программа должна послать в микросхему код, сообщающий, будет ли про11Сходип, чтение или запись в контроллер НГМД. Этот однобайтовый код равен 46Н для чтения и 4АН для записи. Он должен быть послан в каждый из двух портов с адресами ОВН и ОСН. Каждый канал микросхемы 8237 использует три регистра. Один 16-битовый регистр, регистр счетчика, содержит число передаваемых байтов данных. Ero величина должна быть на единицу меньше, чем требуемое число байтов. Для канала 2 доступ к этому регистру осуществляется через порт 05Н; пошлите в неrо два последовательных байта, цричем сначала младший байт. Остальные два регистра содержат адрес буфера в памяти, с которым будет происходить обме1;1 данными. Этот адрес задается как 20-битовое число, поэтому, например, адрес 3000:ABCD зада­ ется как ЗАВСD. Младшие 16 бит посылаются в регистр адреса, который для канала 2 имеет адрес порта 04Н. Сначала посылается младший байт. Старшие 4 бита идут в регистр страницы, котор1,1й для канала 2 имеет адрес порта 81Н. Когда байт посылается по этому адресу, ro значение имеют- только 4 младших бита. Если буфер создается в сегменте данных, то вам нужно сложить значе­ ние DS и смещение буфера для получения 20-битового значения.
Дис,совые накопители 345 Сложение может привести к переносу в значении регистра стра;. ницы. Например, если DS равен lF00H, а смещение буфера - 2000Н, то результирующий адрес будет равен: IF00. + 2000 • = 21000Jf. После того к.ак эти три регистра установлены, пошлите 2 в порт . с адресом ОАН, чтобы разрешить канал 2. Это оставляет микросхему DMA в состоянии ожид;.tния данных от накопителя, а программа должна немедленно начать посылку командных байтов в контроллер НГМД. Вот краткий перечень шагов при программи­ ровании микросхемы 8237. 1. Послать код чтения или записи. 2. Вычислить 20-битовый адрес памяти буфера, в которt,rй будут посланы данные, и заслать его в регистры адреса м сtра-}fицы канала 2. 3. Поместить значение числа, передаваем1,1х байтов <минус 1) в регистр счетчика канала 2. 4. Разрешить канал. ,.. После посылки командных байтов снова ожидайте прерывания и обращайтесь с ним так же, как и после операции поиска. Затем · прочитайте байты статуса. Они таковы: ' Оnерацш, # байта Функц~1я По11ск нет Чтение 1 байт ста.туса О 2 байт статуса 1 3 байт статуса 2 ' 4 номер дорожки 5 номер голоnки 6 номер сектора 7 ко~ б.'1йто11 11а сектор Ю-3)' Запись 1-7 то же, •по ~· ДJIЯ •пе11~1я Вот значения битов трех байтов статуса: Байт статуса 1: • биты 7-6 00 нормальное завершение 01 начато выполнение, не может завершиться 1О неверная команда 11 не выполнено, так как накопитель не nодкл~очен
346 5 4 3 выполняется операция поиска ошибка накопителя накопитель не готов 2 номер выбранной головки 1-0 номер выбранного накопителя Байт статуса 2: биты 7 = номер затребованного сектора больше максимума не используется (всегда О) 6 5 1 ошибка передачи данных 4 переполнение даш1ых 3 не используется (всегда 0) 2 1 не может найти или щю•штап, сектор 1 не может записать из-з.'1 защ~1ты от записи О отсутствует адресная метка при форматизацш1 Байт статуса 3: биты 7 6 5 4 3 2 1 о не используется (всегда 0) встречена адресная метка удалс1111ых щ111111,1х ошибка циклического контрош1 •1ст1юсп1 ;щ1111ых проблема с иденп1фикацнсй дорожки условие коман.ды сканиро11шшя удо11лет11орс1ю услови4? команды сканщюш111иi1 не удо11лст11Оре110 ПJJОхая дорожка = отсутствует адресная метка Глава 5 Как видите, больш;~я часть информации относится к формати­ рованию диска, которое нас в настоящий момент нс интересует. Однако имеется еще четвертый байт статуса, который содержит полезную информацию: Байт статуса 3: биты 7 6 5 4 3 2 1-0 ошибка накопителя диск защищен от записи накопитель 1'0тов текущая позиция l'OJIOIIKИ ИЗПССТ\Ш дискета д11ухсторош1яя номер выбрашюf1 голо11ки номер выбрашюrо IШKOIIИTCJIII Вы можете получить четвертый байт статуса, послав контрол­ леру НГМД команду "Определи статус накопителя" (Sensc Drive
· Дисковые на,:опител.и 347· · status). Первый байт этой двухбайтовой команды - это число 4, а второй байт содержит номер накопителя в битах 1 и •О и номер rоловки в бите 2. Единственным результатом этой операции явля­ ется байт статуса 3. Отметим, что после ·каждой дисковой опера­ ции,· если вы используете п~цедуры DOS или BIOS, результиру­ ющие байты статуса помещаются в область_ данных BIOS, начиная с адреса 0040:0042. Операционная система хранит также байт статуса дискеты по адресу 0040:0041, значение битов· котороrо следующее: Значение ·бита 80Н 40Н -20н l0H 09Н 08Н 04Н 02Н ОlН Ошибка нет ответа на присоединение накопителя юпераци11 поиска неуспешна ошибка контрол.лера НГМД ошибка _данных при чтении (ошибка CRC) попытка прямого доступа за границу 64К переполнение DМА затребованный сектор не найцен не найдена адресная марка пос.лана неверная команда контрол.леру НГМД , В заКJ,Iючение приводим полную процедуру чтения диска, кото­ рая читает один сек~р данных с дорожки 12, сектора 1, стороны О накопителя А в 512-байтовый буфер в сегменте· данных. Семь байтов статуса также сЧИТЬ1ваются •в отведенный буфер. Эrа про­ цедура предназначена для IВМ РС.и ХТ. Вам-необходимо восполь­ зоваться техническим руководством по PCjr или АТ, _ес,11и вы рабо­ таете на этих машинах. На АТ надо из~енить ЦИКJIJ,il задержки, чтобы учесть большую скорость процессора, и не забывать добав-· лять оператор JMP SHORT $ +2 между последовательными коман­ дами OUT, относящимися к одному и тому же порту. Работа с фиксированным диском осуществляется аналоmчно, поэтому вы можете применить изученные вами концепции в друrих ситу­ ациях. - ;---в сеrменте данных BUF DB 512 DUP(?) STA_BUFDB 7 DUP(?) S_READ PROC ;---включение мотора SТI ., MOV • DX,3F2H ;начало процедуры чтения.одного сектора ;прерывания допжны быть разрешень1 ;адрес реmстра цифрового вывода
348 Глава 5 MOV AL,28 ;усташ111шшаем б1пы 2, ·з 11 4 OUT DX,AL ;посылаем кома1щу ;---ожидаем, пока мотор наберет скорость (ок9ло 1/2 секу1111ы) MOV СХ,3500 ;счетчик цикла задержки (w111 ШМ РС 1,1..ХТ) MOT_D: LOOP MOT_D ;ожидаем 1/2 секу11ды ;---выпо;шяем операцию поиска MOV АН,15 CALL OU FDC MOV АН,0 CALL OU FDC ·моv АН,12 CALL OU FDC ;1юмер кода ;посылаем контроллеру I IГМД ;номер накопителя ;посылаем ко11троллеру I-IГМД ;номер дорожки , ;посылаем ко11троллеру НГМД ;ожидаем nрерыщшия от НГМД ;---ожидаем установки головм1 (25 мс) CALL WA INT MOV СХ,1750 WA_SET:WOP WA_SET ;счетчик цикла задержки (дл11 ШМ РС и ХП ;ожидаем 25 мс ;---начинаем инициализацию микросхемы ОМА MQV AL,46H OUT 12,AL OUT 11,AL ;---вычисляем адрес буфера ;код чте11ш1 да11111,1х контроллера НГМД ;посылаем код по дпум адресам MOV AX,OFFSET ВUF;берем смеще1111е буфера 11 DS MOV BX,DS ;помещаем DS в ВХ MOV • CL,4 ;готовим _вращеш1е старшего 11ибла ROL • BX,CL MOV AND AND ADD JNC INC NO_CRY:OUT моv OUT моv OUT DL,BL DL,0FH BL,0F0H АХ,ВХ NO_CRY DL 4,AL ЛL,АН 4,AL AL,DL 81Н,АL ;---конец· инициализации MOV OUT АХ,511 5,AL ;вращаем м;шдu111е 4 б1па ;кош~руем DI, в BL ;•111сн1м старший 1111бл в DL ;•~исн1м младший 1шбл 11 ВХ ;склады паем ;если 11е бы;ю пере1юса, то # страшщы 11 Dl, ;увеличи~'\ем DL, если был пере11ос ;посылаем младший байт адреса ;сдвигаем старший байт ;посылаем младшиi'1 ·баi'п адреса ;засылаем номер страшщы ;посылаем 1юмер стра11ицы ;значение счетчика ;посылаем младший бай11
·дис,совые накопители MOV AL,AH OUT S,AL MOV AL,2 OUT 10,AL ;готов~1м старший байт ;посылаем старший байт ;готов~1м разре111е11ие кащ1ла 2 ;DMA ожида~т дан11ые ;---получаем указатель на базу диска 349 MOV AL,IEH ;номер вектора, указывшощего на таблицу MOV АН,ЗSН ;номер функции , 1NТ 2lH ;выполняем ·функцию ;---посылаем параметры чтения моv АН,66Н ;код чте,тя одiюго·сектора CALL !)U_FDC ;посылаем контроллеру НГМД MOV АН.О ;номера головки н 11акоп~~теля CALL OU FDC ;посылаем контроллеру нгмд MOV АН,12 ;1юмер дорожки CALL QU, ...F DC ;посылаем ко11'rроллеру НГМД MOV АН,О ;номер l'ОЛОВКИ CALL OU_FDC ;посылаем ко11Троллеру НГМД MOV АН,! ;номер З.'lШ/СИ CALL OU_FDC ;пось,лаем контроллеру нгмд MOV AH,ES: [ВХ] + 3 ;код размера сектор.'~ CALL OU_FDC ;пось~лаем. ко11троллеру НГМД моv AH,ES: [ВХ) + 4 ;номер ко11ца дорожки CALL OU FDC ;посылаем ко1~троллеру НГМД MOV AH,ES: [ВХ] + 5 ;длина сд11ю11 CALL OU_FDC ;посыJ1аем ко~проллеру НГМД MOV AH,ES: [ВХ] + 6 ;длина дш111ых CALL OU FDC ;посылаем контроллеру НГМД CALL WA INT ;ожидаем прерьшани~ от НГМД ;---читаем результирующие байты MOV СХ,7 ;берем 7 байт статуса LEA BX,STA_BUF NEXT: CALL IN_FDC MOV [BX],AL INC вх LOOP NEXT ;---выключение мотора MOV I DX,ЗF2H MOV AL,12 OUT DX,AL ;помещаем в буфер статуса ;полу•1аем байт ;помещаем 11 буфер ;указ1,111аем 11а следующий ба~~т буфера ;повторяем 011сршtию ;адрес ре1·истра цифро1юl'О 11ы1ю,щ ;оставляем б~~ты З н 4 ;посылаем новую установку
350 RET S_READ ENDP Глава 5 ;конец IIроцедуры WA_INT PROC ;ожидание прерывания от НГМД ;---уnрамение статусом прерывания 6 в байте стаtуса BIOS MOV АХ,40Н ;сегмент области данных ВIOS MOV ES,AX ;помещаем в ES MOV ВХ,ЗЕН ;смеще_ш1е для байта статуса AGAIN: MOV DL,ES:[BXJ ;полу•шем байт TEST DL,80H ;проверяем бит 7 JZ AGAIN ;до тех пор, пока не уста~ю11JIен AND DL,0II\111\B ;сбрасьш,1ем бит 7 MOV ES:[BX),DL ;заменяем байт статуса RET WA INT ENDP OU_FDC PROC ,;пось~лаем байт из АН FDC MOV: DX,3F4H ;адрес порта регистра статуса КЕ TR: IN AL,DX ;nолу•шем з1ш•1еш1е TEST AL,128 ;бит 7 устаноме11? JZ KE' _TR ;если нет. то с1ющ1 nро11ер~1ем INC DX ;указып.аем 1111 ре1·~н:тр _1щ1IIIых MOV AL,AH ;переданаемое з1ш•Iение 11 ЛI 1 OUT DX,AL ;nOCIJIJlaeм ЗIШIICIIИC RET OU_FDC ENDP IN_FDC PROC ;полу'lаем байт от FDC в AL моv DX,3F4H ;адрес порта ре1·истра статуса ON_AG: IN AL,DX ;nолу•1аем з1ш•1е11ие TEST AL,128 ;_бит 7 устаномен? JZ ON_AG ;есш1 нет, то nронеряем с1ю11а INC DX ;указываем на регистр щ,ншых IN AL,DX ..... ;•1итаем байт ~•з реl'истра да11111,Iх RET IN_FDC ENDP 5.4.1 . Чтение/запись определенных секторов Чтение и.ли запись определенных секторов диска в основном используется при доступе к каталогам диска или его таблице размещения файлов, сектора для которых всегда расположены в одном и том же месте. В то время _как чтение секторов достаточно
дисковые накопители 351 безобидно, запись абсолютного сектора требует, чтобы код был тщательно проверен перед работой. Ошибка может сделать каталог или таблицу размещения файлов нсчитаемыми, что :жвив.tлснтно разрушению всех данных на диске. Как DOS, так и BIOS прсдоставл11ют функции для чтения и записи определенных секторов. Однако они указывают сектора по­ разному. Для IВМ РС, ХТ и PCjr процедура BIOS требует информацию о номерах стороны (0 или 1), дорожки (0-39) и сектора (1-8). Из-за ограничения максимального номера сектора, равного 8, этот метод практически бесполезен для перечисленных машин. Однако для АТ номер сектора может меняться до 8, 9 или 15, а число дорожек - до 39 или 79. Функции DOS указывают сектор одним номером, который называется логиlfески.м ,ю.меро.м сектора. Начиная с наружного обода диска, секторам присваива­ ются последовательно возрастающие номера. Этот метод может быть использован для дисков произвольного размера и типа. Отсчет логических секторов начинается со стороны О дорожки О сектора 1 и продолжается на стороне I с дорожки О, после чего переходит на сторону О дорожку I и т.д. (На больших фиксиро­ ванных дисках сначала проходи-тся весь внешний цилиндр.) В зависимости от того, как был форматирован диск, при переходе на следующую дорожку логический номер сектора увеличивается на определенную величину. Для дискет емкостью 360К каждая дорожка (с учетом обеих сторон) добавляет к логическому номеру 18. Однако вычисления немного усложняются тем, что отсчет начинается с нуля. Таким образом, первый сектор на дорожке 3 стороны 2 должен иметь номер, равный 3 х 18, для дорожек 0-2 плюс 9 для стороны О дорожки 3 плюс единица, указывающая на первый сектор дорожки 3 стороны 1. Эта сумма равна 64. Логи­ ческий номер сектора на 1 меньше этого числа. На рис. 5.4 срав­ ниваются методы указания сектора DOS и BIOS. Высокий уровень Бейсик не предоставляет прямого доступа к секторам диска. Надо использовать соответствующую процедуру на машинном языке. В приложении Г объясняется логика взаимодействия с этоi: процедурой. В примере читается 9 секторов дорожки 3 стороны 1 дискеты емкостью 360К. Сама процедура размещается в памяти, начиная с адреса сегмента &Hl00O, а содержимое секторов разме­ щается, начиная с сегментного адреса &Н2000 (напоминаем, что абсолютный адрес равен сегментному адресу, умноженному на 16). Для того чтобы записать на диск содержимое этоrо буфера, надо изменить в данных программы седьмой байт с конца &Н25 на &Н26. Все остальное остается неизменным.
352 Нумерацм11 BIOS Нумерацм11 DOS Глава 5 Сторона О 11п11 1 Включает обе стороны Рис. 5.4. Нумерация секторов днска, нс11ользусм:111 ВIOS ~• DOS
Дисковые накопители 100 DEFINT A-Z ·все nеремен111,Iе будут 11сm,Iм~I 110 DATA &Н55, &Н8В, &НЕС, &Н\Е, &Н88, &1176, &II0C, &IIIШ 120 DATA &НО4, &Н8В, &I-176, &НОЛ, &1-188, &НI4, &IIXB, &1176 130 DATA &Н08, &J--188, &НОС, &1'188, &1-176, &1-106, &IIXЛ, &IIIC 140 DATA &Н8Е, &HD8, &Н8В, &НС3, &1-188, &НОО, &1-100, &IICD 150 DATA &Н25, &Н59, &НIF, &H5D, &НСА, &НО8, &1100 353 160 DEF SEG = &HIOOO ·помещаем npoiieдypy по адресу &HIOOOO 170FORN=ОТО38 ·для каждого байта npo1ieдypI,I 180 READ Q: РОКЕ N,Q ·•шtаем байт и nомеIщ1см eIu 11 ш1мят~, • 190, NEXT ·следующий байт 200 READSECTOR = О ·выnолвяем, код, на 1 Iшшя с 11срIюrо байта 210 BUFFER = &I-12000 'буфер для да~шых ИJl:feeт адрес &1120000 220, LOGICALNUMBER 62 ·логический номер сектор.'! р,шсII 62 230 NSECTORS = 9 ·•шсло,с•шт~,шаемых cekтoJ){1I1 240 DRIVE = О ·1юмср накопителя (0 = Л) 250 CALL READSECTOR ШUFFER, LOGICЛLSECTORS, NSECTORS, DRIVE) 260 .'теперь сектора в памяти, вачищ1я с адреса 2000:0000 Средний уровень BIOS использует функцию 2 прерывания 13Н для чтения секторов и функцию 3 прерывания 13Н для записи секторов. В обоих случаях DL должен содержать номер накопителя от О до 3, где О = А, 1 = В и т.д., DH - номер головки (стороны), 0-1. СН должен содержать номер дорожки от О д_о 39, а CL - номер сектора от О до 8. AL содержит число секторов, которое необходимо счи­ тать. Допускается сразу читать не более восьми секторов,. что вполне достаточно для большинства целей. ES: ВХ должны vказы­ вать на начало буфера в памяти, куда будут· помещаться данные или откуда они будут браться. При возврате AL будет содержать число прочитанных или записанных секторов. Если операция завершена успешно, то флаг переноса будет равен нулю. Если он.· ' равен l, то АН будет содержать байт статуса дисковой операции, описанный в (5.4.8 ]. ;---в сеrмевте даввых BUFFERDB 4000 DUP(?) ;---читаем сектора ;создаем буфер MOV AX,SEG BUFFER ;ES:RX должш,I указI,Iщ1п, IIа буфер MOV ES,AX MOV BX,OFFSET BUFFER MOV DL,0 ;11омер вакопителя MOV DH,0 l 2 Р. Джордейн ;11омер ГOJIOllKИ
354 моv си.о MOV CL,l MOV AL,l MOV АН,2 INT IЗН ;номер дорожки ;номер сектора ;число секторов ,1!1Я чте1н1я ;номер фу11кцш1 •11еш1~1 Глава 5 Прерывания DOS 25Н- и 26Н читают и записывают абсо,,ютные сектора диска соответственно. Надо поместить логический номер стартового сектора в DX, а DS:BX должны указывать на· буфер. СХ содержит число секторов для чтения или записи, а AL - номер накопителя, где О = А, 1 В и т.д.- Процедуры портят все регистры, кроме сегментных. При возврате регистр флагов остается на стеке, оставляя стек невыровненным. Не забудьте убрать это значение со стека сразу после возврата (в примере это значение убирается в СХ). ;---в сегменте данных BUFFERDB DUP 5000(?) ;создаем буфер ;---читаем сектора PUSH DS ;сохр;шясм регистры MOV AX,SEG BUFFER ;DS:BX долж11h1 указ1,111ап, ш1 буфер MOV DS;AX MOV BX,OFFSET BUFFER MOV DX,63 ;логическ~1й номер сектора MOV СХ,9 ;•штаем всю дорожку MOV AL,0 INT 25Н РОР СХ РОР DS JNC NO_ERROR СМР АН,3 NO_ERROR: ;накошпель А ;функция чтения секторов _;убираем из стека флаги ;восстанавливаем регистры ;если нет ошибк~1. то 1ia продолжение ;проверка возможных ошибок ;продолжение программы Если при возврате флаг переноса равен 1, то произошла ошибка и в этом случае АН и AL содержат два отдельных байта статуса ошибки. Если АН = 4, то указанный сектор не найден, а если АН = 2, то диск неверно отформатирован. Если АН = З, то была попытка записи на дискету, защищенную от записи. Все остальные знач_ения АН говорят об аппаратной ошибке.
Дисковые накоп.ители 355 Низкий уровень Дисковые операции на низком уровне требуют прямого прог­ раммирования микросхем контроллера НГМД и прямого доступа к памяти. Поскольку эти операции взаимосвязаны, они рассмат­ риваются вместе в параграфе [5.4.1 ). S.4.3 . Запнсь в последовательные файлы С точки зрения программиста язык1:1 высокого уровня работают с последовательными файлами порциями в одну единицу данных. Один оператор "записывает" содержимое переменной в последова­ тельный файл,_ ограничивая ее парой возврат каретки/перевод строки. С другой стороны, программы, написанные на языке ассем- 1блера, имеют дело с данными, измеряемыми в единицах записей. Они помещают данные в буфер, который может содержать одну или несколько записей, добавляя пары возврат каретки/перевод строки между элементами данных, а не между заш1сями. Неко­ торые элементы данных могут принадлежат~, двум записям. Тогда для записи используется функция MS-DOS, позволs1ющаs1 записат,, на диск одну или несколько записей. На всех уровнях програм­ мирования DOS может не производить физической записи на диск каждый раз, когда была подана команда вывода. Вместо этого в целях экономии DOS ожидает, пока его выходной буфер будет заполнен, прежде чем записать данные на диск. Отметим, что Бейсик автоматически добавляет в конец записы­ ваемого им последовательного файла символ с кодом ASCII 26 (Ctrl-Z). Это требование стандартных текстовых файлов. Функции· DOS не добавляют этот символ; ваша программа должна сама записать его в конец элемента данных. Файлы прямого доступа не ограничиваются символом ASCII 26. ~ысоки~ уровень Бейсик готовит файлы к последовательной записи, открывая _ файл в режиме последовательного доступа оператором OPEN. Этот оператор имеет две формы и выбор одной из них целиком зависит от вашего вкуса. Форматы оператора OPEN таковы: 100 OPEN "MYFILE" FOR OUTPUT AS #1 или 100 OPEN "О", #1, ... MYFlLE" 12*
356 Глава 5 Во второй форме буква "О" обозначает вывод (output). Символ # 1 обозначает кодовый номер, по которому вы будете впоследствии обращаться к файлу в операторах доступа, таких, как WRIТE # 1 или INPUT #1. В обоих случаях открывается файл с именем MYFILE для приема данных в последовательном режиме. Ее 1и файл с таким именем не найден на диске, то оператор OPEN создаст его. Если же файл существует, то он будет перезаписан, т.е. после закрытия он будет содержать только новые записанные в него данные. Чтобы добавить данные в конец сvществvющего последовательного файла, не изменяя его ·предыдущего· содер­ жимого, нужно открыть его, используя первую форму оператора OPEN в виде OPEN "MYFILE" FOR APPEND AS 1. Более подробно об этом см. в [5.3.З ]. Данные записыsаются в файл с помощью операторов PRINT # и WRIТE#. Они имеют одинаковую форму: НЮ PRINT #1, S$ или 100 WRIТE #1, Х # 1 относится к идентификационному номеру файж1 (дескриптору файла), присваиваемому ему оператором OPEN. В первом примере в файл записывается значение строковой переменоой, а во втором - численное значение, но можно любым из операторов записыват1> и то, и другое. Численные значения записываются в после;:юва­ тельные файлы в строковом виде, хотя они и берутся не и:з стро­ ковых п.еременных. Например, 232 яsлястся двухбайтовым целым в строковой форме, однако если Х = 232, то опер;.~тор PRINT #l, Х помещает в файл три байта, используя коды ASCI! для цифр 2, Зи2. Операторы PRINT # и WRIТE # отличаются друг от друга спо­ собом отделения элементов данных в файле. Какой из них более подходящий, определяется характеристиками данных. Основное различие между двумя операторами состоит в том, что WRITE# вставляет дополнительные ограничители междv ::>,1ементами дан­ ных. Рассмотрим случай, коrд~i оператор выводит нсско.11,ко пере­ менных в виде 100 PRINT #1, А$, Z, В$ или 100 WRIТE #1, А$, Z, В$. В этом примере пара возврат каретки/ перевод строки будс.-т помещена в файл только за последней из трех переменных (отмс­ тим, что строковые и числовые переменные могут быть переме­ шаны). Как же можно впоследствии выделить ли три пере­ менные? Если был использован оператор PRINT #, то никак. Все три переменные будут объединены в непрерывную строку. Если же
Дисковые накопители 357 был использован оператор WRITE #, то каждый элемент ,;:ханных будет заклЮ,чен в кавычки, а между ними будут стоять запятые. Затем, rtpи чтении этих элементов из файла, Бейсик будет автома­ тически удалять кавычки и запятые, которые были добавлены оператором WRIТE #. Кроме того; имеется еще ряд менее важных вопросов. Один из них состоит в том, что вся проблема с ограничителями может быть снята, если использовать для вывода каждой переменной от,:~е,1ь­ ный оператор PRINT# или WRIТE#. В этом случае PRINT# будет отделять все элементы парами возврат каретки/ перевод строки, а WRITE # будет делать то же .самое, но по-прежнему каждый элемент будет заключен в кавычки (что напрасно расхо­ дует файловое пространство). Более того, для вывода строк, кото­ рые •сами содержат кавычки, оператор WRITE # использовать нельзя, поскольку первая же • внутренняя кавычка будет при чтении ошибочно воспринята как признак конца переменной. И наконец, отметим, что когда в одном операторе выводится несколько переменных, то они оба форматируют данные в точ­ ности так же, как они форматировались бы при выводе на терми­ нал. Таким образом, PRINT # 1, А$, В$ отделяет В$ от А$, в то время как PRINT #1, А$; В$ - нет; файл будет дополнен нужным числом пробелов. Оператор PRINT # может быть испол1,зован в форме PRINT #1 USING ... , при этом могут быть задействованы все обычные экранные форматы PRINT USING для форматиро­ вания вывода в файл. Вообще говоря, более экономично применять оператор PRINT #, записывая каждый раз по одной переменной. Этот метод избавляет от излишних ограничителей и позвою1ет затем безоши­ бочно считывать строки любого вида. Более сложные схемы огра­ ничителей, используемые при записи нескольких переменных одним оператором PRINT # или WRIТE #, могут при.вести к проб­ лемам, особенно если одна переменная будет считана как две, что приведет к потере текущей позиции в файле. После того как все данные будут записаны в файл, просто зак­ ройте его, чтобы обезопасить содержащиеся в нем .:ханные. Напи­ шите CLOSE, чтобы закрыть все открытые файлы, CLOSE # 1, чтобы закрыть файл #1, и CLOSE #1, #3, чтобы закрыть файлы # 1 и #3. Хотя. в некоторых случаях Бейсик не требует закрытия Файлов, но это не соответствует описанной выше ситуации. Опера­ торы WRITE # и PRINТ # выводят данные в файловый буфер, который записывается на диск только тогда, когда он заполнен информацией. Последние введенные данные записываются н~1 диск оператором CLOSE. Отсутствие ::rroro оператора может привести к потере данных. Например:
358 100 OPEN "A:NEWSEQ" FOR OUTPUT ЛS #1 110 А$ "ааааа" 120 В$ = "ЬЬЬЬЬ" 130 С$ = "ссссс" 140 WRITE #1, А$, В$, С$ 150 CLOSE Средний уровень Глава 5 'открываем файл I·оттI~Iм три строки ·запис1, строк 'очистка буфера MS-DOS может писать последовательные файлы как методом управляющего блока файла, так и методом дескриптора файлов. Метод FCB предоставляет функцию, специально сконструиро­ ванную для записи последовательных файлов. Метод же дескрип­ тора файлов имеет только функцию записи в файл общего назна­ чения, но ее легко использовать и для этой цели. В любом случае важен способ, с помощью которого был открыт последовательный файл. Если данные добавляются к последовательному файлу, то должна быть использована обычная функция открытия файла. Однако ·если файл перезаписывается заново, то требуется функция "создания" файла. Эта функция "обрезает" файл до нулевой длины, поэтому она будет равна длине записанных в него данных. Метод FCB. Функция 15Н прерывания 21 Н предназначена для записи в последовательный файл. Надо подготовить управляющий блок файла и область обмена с диском, как показано в [5.3 .51. Если файл должен быть перезаписан, то его надо открыт~, с помощью функции lбН, которая "создает" файл, "обрезая" его до нулевой длины. Если вы откроете файл с помощ1,ю функции OFH, то остаток старого файла останется в конце файла, сели длина нового файла будет меньше старого. С другой стороны, если вы хотите добавить данные к файлу, то используйте функцию открытия файла. После того как файл открыт, вы должны установить DS:DX на начало FCB и вызвать функцию 15Н, для того чтобы вывести одну запись данных. Количество данных в записи зависит от величины, которая помещена в поле длины записи, расположенное со смеще­ нием 14 в обычном FCB; по умолчанию это значение равно 128 байтам. Если размер записи меньше, чем размер сектора диска - 512 байт, то данные будут буферизоваться до тех пор, пока их нс накопится достаточно, чтобы произвести реал1,ную запись на диск; поэтому записи в последовательный файл могут успещно заnисы­ ваться,даже если накопитель не включен. При закрытии файла вес данные, оставшиеся в буфере, сбрасываются на диск. При возврате из функции 15Н, AL равен О, если операция успешна, 1, если диск полон, и 2, если сегмент области обмена данных слишком мал.
Дисковые нак,опители 359 8 следующем Примере на ДИСК ВЫВОДЯТСЯ 5 записей ДЛИНОЙ 256 байт. Записи могут быть набором текстовых данных. Эти данные расположены в области памяти, помеченной меткой WORKAR. В первый момент указатель на DTА устан~вливается на начало этой области, а после вывода каждой записи установка DTА меняется таким образом, чтобы он указывал на 256 байт выше. Отметим, что обычно для такой рабочей области отводится специальная область памяти (см. [1.3 .1 ]) , но в данном примере для простоты используется буфер, расположенный в сегменте данных. ;---в сегменте данных WORКAR 08 FCB 08 2()(Ю OUP (?) ;буфер да11111,Iх 1 ,'FILENЛMEEXT ,25 OUP (0) ;---ОТЛ должен указывать на рабо•1ую область LЕЛ OX,WORКAR ;OS:OX указывают на ОТЛ MOV Dl,OX MOV ЛН,IЛН INT 21Н ;---открываем файл MOV АН,I6Н LЕЛ OX,FCB INT 21Н ;сохраняем кош1ю ;функция. установки ОТЛ ;устанаwшваем ОТЛ ;номер функции ;OS:OX указыв.'lют на 1°СВ ;открываем файл ;---устанавливаем размер записи LЕЛ BX,FCB ;ВХ указы11ает 1ia l'CB MOV ЛХ,256 MOV [ВХ] + 14,ЛХ ;---посылаем данные в файл MOV СХ,5 NEXT_R: моv ЛН,I5Н LЕЛ OX,FCB INT 21Н СМР ЛL,2 JE CONTINUE СМР ЛL,1 ;размер запис11 - 256 байт ;запнсыпаем u поле размера записи ;число записей ;функция З.'\ш1си ;указываем 11а FСЦ ;заnнсыщ1ем дан111,Iе ;пропсрка 11а 011шбк~1 ;и их обработка ;д11ск 1юJю11? JE OISK_FULL ;на процедуру обработк11 1ютю1'(> 1111ска ;---перенос выполнен, ·переустанаwш11аем ОТЛ ЛОО 01,256 MOV OX,DI MOV АН,IЛН INT 21Н LOOP NEXT_R ;сдвигаемся 1111 1 зашlСI, ;OS:OX указывают на новый ОТЛ • ;функция установки О ТЛ ;установка новой позшtни ;переходим к следующей записи
360 :---позднее закрываем файл LEA DX,FCB MOV AH,IOH INT 21Н ;DS:DX указывают на l'CB ;фу11к11ш1 закр1,пш1 фа(1:~а ;закрываем файл Глава 5 Метод управляющего блока файла не слишком , удобен для добавления записей в конец существующего последовател1,ноrо файла. В отличие от метода дескриптора файла, который позво­ ляет указать на конец файла, здесь вы должны манипулировать полями текущей записи и текущего блока. Нужно считать послед­ нюю, несущую информацию, запись в DT А, а потом заполнит~, пустое пространство в нем первой записью данных, которые вы хотите добавить. Затем перезапишите запись на ее старое место в файле, после чего вы можете добавлять скол1,ко хотите новых записей. Файл должен быть открыт функцией 0FH. Метод дескриптора файла. Необходима вним~1тел1,носп, при открытии файла для последовательного вывода методом дескрип­ тора файла. Поскольку та же самая функция испол~,зуется для записи в файл прямого доступа, то при закрытии файла его длина не устанавливается равной последней позиции файлового указа­ теля. Возьмем, например, случай, коrда текстовый файл размером 2000 байт считывается с диска, а затем в процессе обработки в памяти его длина уменьшается до l ООО байт. Если файл бы~, открыт простой командой открытия файла (функция ЗОН), то после того, как новая, более короткая, версия файла будет запи­ сана на диск и файл будет закрыт, его длина останется равной 2000 байтам, из которых новый текст будет занимать первую тысячу байтов. По этой причине при открытии последовател~,ноrо файла для перезаписи надо использовать функцию ЗСН преры­ вания 2IH (см. [5.3 .2 ]). Эта функция обычно создаст новый ф,1йл, но если файл уже существует, то он Нобрезается" до нулевой длины. Для добавления данных в последовательный ф.~йл надо применять обычную фу~,кцию открытия файла - ЗDН прсрываниs1 21Н (см. [5.3 .3 ]). Рассмотрим сначала случай полной перезаписи файла. Если файл был открыт с помощью функции 3СН, файловый указатель устанавливается равным нулю, поэтому отпадает необходимость в его установке. Поместите номер файла в ВХ, а число записыва­ емых байтов - в СХ. Затем установите DS:DX на первый байт выводимых данных и выполните функцию 40Н прерывания 21 Н. При возврате, если флаr переноса установлен, то была ошибка и АХ содержит 5, если была ошибка дискового накопителя, и 6, если неверный номер файла. В противном случае АХ будет содержать число реально записанных байтов; при несовпадении, вероятнее
Дисковые накопители 361 всего, проблема ,состоит в том, что диск полон. Не забудьте о про­ цедуре восстановления при сбоях программы, поскольку в этом случае первоначальное содержимое файла будет утеряно, так как он был "обре~н" до нулевой длины. Как проверять дисковое пространство,описано в [5.1 .2). Например: ;---в сегменте ~анных РАТИ DB 'B:FILENAМE.EXТ',0 ;путь к ф.-.йлу BUFFER DB 2000 DUP (?) ;---открываем файл с помощью функции "создания" LEA . DX,PATH ;DS:DX указывают на путь к ф.-.йлу MOV СХ,О ;атрибуты ~йла (здесь обыч111,1е) MOV АН,ЗСН ;номер функции INT 21Н ;открываем ф.-.йл JC OPEN_ERROR ;уход по ошибке MOV HANDLE,AX ;запоминаем номер файла . ;---записываем в файл 1000 байт MOV АН,40Н ;номер функции MOV BX,HANDLE ;номер файла в ВХ MOV СХ,1000 ;число байтов, которое надо заш1сать LEA. INT JC СМР . JNE DX,BUFFER 2IН OUT_ERROR СХ,2000 FULL_DISK ;DS:DX указывают на буфер да11111,1х ;заnисыв.-.ем дшшые ;проверка на ош~1бки ;обработка оuшбок . Для добавления. записей в последовательный файл надо отк­ рыть файл с'помощью функции ЗDН прерывания 21Н, помещая 1 в AL, если программа будет только писать данные, и 2, если •прог­ рамма будет и читать, и писать.. Длина файла остается неиз­ менной, хотя он будет увеличиваться по мере добавления данн.ых. Файловый указате.,~ь должен быть установлен на конец файла, иначе существующие данные будут перезаписаны. Это выполня­ ется функцией 42Н прерывания 21Н. Поместите номер подфун­ кции 2 в AL для установки указателя на конец файла, а номер файла помести~rе в ВХ. CX:DX указывают на смещение относи­ тельно конца файла, .начиная с которого будет производиться запись, поэтому обнулите эти регистры. Затем выполните функ­ цию установки указателя. При возврате установленный флаr пере­ носа индицирует ошибку, при этом в АХ будет находиться l, если номер подфункции в AL был неверен, и 6, если. неверно был
362 Глава 5 указан номер файла. После того как файловый указател1, установ­ лен, операция записи выполняется, как и в предыдущем случае: ;---в сегменте данных РАТИ DB ·в:FILENAME.EXT',0 BUFFERDB 1000 DUP(?) ;---открываем файл ;пуп, к файлу LEA DX,PATH MOV AL,I MOV АН,ЗDН INT 21Н JC OPEN_ERROR. MOV HANDLE,AX ;DS:DX указы па ют н:1 11у п, ;код открытия то;н.ко д..,•,я за111-1\..t. ;номер фу11к11ш1 ;открываем файл ;ух~д по ошибке ;сохраняем 1юмср файла ;---установка файлового указателя на конец файла MOV ВХ,АХ ;номер файла II ВХ MOV СХ,О ;CX:DX дают смеще11нс опюситет,1ю ко111щ MOV DX,0 MOV AL,2 ;код для кшща файла MOV АН,42Н ;фу11кцf1я установки указателя INT 21Н ;устанавливаем указате:11, JC POINT_ERROR ;проверка на 0111ибку ;-.--добавляем к файлу 300 байт MOV АН,40Н ;номер функцш1 MOV BX,HANDLE ;номер файла в ВХ MOV_ СХ,300 ;число записываемых байтов LEA DX,BUFFER ;DS:DX указывают 11а буфер :щ11111,1х INT 21Н ;добавляем д:111111,1е JC OUTPUT ERROR :проверка щ1 шш1бкf1 СМР СХ,300 JNE FULL_DISK ;обработка 01ш1бок ;Прl-1 ИХ IШЛН'IИИ S.4 .4 . Чтение нз последовательных файлов Чтение ~з последовательного файла мало чем отличается от записи в него, за исключением того, что этот процесс - обратный. В Бейсике данные берутся из файла и присваиваются отдельным переменным или элементам массива данных. В языке ассемблера данные помещаются в буфер, расположенный в памяти. В послед­ нем случае данные передаются по записям и программа ,:юлж,на сама выделять элементы данных, составляющие записи (под
Дисковые накопители 363 записью здесь понимается порция данных, которая считывается из файла). Высокий уровень Чтение последовательных файлов в Бейсике проще, чем их запись, поскольку имеется только две возможности для обращения к ним, в зависимости от того, какие символы в файле исполь­ зуются в качестве ограничителей элементов данных. Оператор INPUT # распознает запятые и кавычки как разделители данных, так же как и пары возврат каретки/перевод строки. Оператор LINE INPUT# распознает только комбинации CR/LF, по:>тому он может использоваться для чтения целых строк текста, содержа­ щих другие ограничители. Эта возможность удобна при обработке текстов. Для чтения трех элементов оператором INPUT # сначала отк~ ройте файл, как обсуждалось в [5.3 .3] (например, OPEN "A:NEWSEQ" FOR INPUT AS #1). Если файл был открыт под номером l, то оператор INPUT # 1, Х$, У$, Z$ присвоит значение первых трех элементов данных трем строковым переменным. При вводе числовых переменных, например INPUT #1, Х, У, Z, необ­ ходимо, чтобы соответствующие данные в файле были числовыми. Число с двойной точностью должно считываться в переменную двойной точности, с тем чтобы она могла хранить восемь байтов такого числа. Другой способ прочитать три элемента данных сос­ тоит в размещении их в массиве: 100 DlM IТЕМ$(40) 11ОFORN=ОТО39 120 INPUT #1, IТEM$(N) 130 NEXT ·создаем массн11 строк нз 40 элсмс1пm1 ·для каждот элемента ·с•н-пываем с1·0 и 11омс11шем II массив Чтобы прочитать п-й элемент последовательного файла, проr­ рамма должна прочитать все предшествующие ему элементы. Для этого надо создать цикл, в котором будут считываться элементы данных без их дальнейшего сохранения. Оператор LINE INPUT # действует в основном аналоrично оператору INPUT #, за исключением тоrо, что он может прини­ мать только одну и только. строковую переменную. Переменная может быть длиной до 254 символов, это максимально допvстимый размер строковых переменных в Бейсике. Пара возвра; карет­ ки/перевод строки, содержащаяся в файле, включается в строку, возвращаемую оператором LINE INPUT #. Это свойство позволяет обнаруживать конец параграфа в текстовом- файле.
364 Глава 5 Функция EOF (конец файла) может быть использована для определения того, все ли элементы файла были прочитаны. EOF возвращает -1, если файл исчерпан, и О - в противном с.,1учас. В функции требуется указать номер файла, под которым он был открыт; например, если файл был открыт как #2, то Х = EOF(2). В следующем примере весь текстовый файл считывается в массив. 100 OPEN "ТЕХТ.ААА" FOR INPUT AS #2 ·открываем файл 110 DIM ТЕХТ$(500) ·не более 500 строк 120 LINECOUNT = О ·счетчик строк 130 LINE INPUT #2, TEXT$(LINECOUNT> получаем строку 140 IF EOF{Z) ТНЕN 170 ·проверка на конец файла 150 UNECOUNT = UNECOUNТ + 1 ·увеличи-ваем счетчик 160 GOTO 130 ·на следующую строку 170 ... 'файл прочитан Оператор INPUT$ читает из последовательного файла указан­ ное число символов. На самой программе лежит забота о выделе­ нии отдельных элементов данных. Формат этого оператора для чтения 30 символов из файла #1 таков: S$ = INPUT$(30,#l). Хотя вы можете указывать число байтов для чтения, необходимо, чтобы это число нс превосходило 254, поскольку это макси­ мальный размер строковой переменной, в которую помещаются данные. Оператор INPUT$ полезен при передаче массы ;э:анных в непрерывную область памяти. В следующем примере первые 200 байт последовательного файла выгружаются в буфер монохромного дисплея, с тем чтобы они бы.ли выведены на экран (включая управляющие коды). 100 OPEN "A:NEWFILE" FOR INPUT AS #1 110 CLS: DEF SEG = &НВООО 120FORN=ОТО9 130S$ = INPUT$(20,#l) 140FORМ=1ТО20 150 РОКЕ N*160 + М*2, ASC(MID$(S$,M ,1) 160 NEXT М 170 NEXT N Средний уровень ·открьшпем файд ·указы11аем на буфер ·получаем·IО rpyiin по 20 байт 'берем каждый байт ·и помещаем em в буфер ·переход к следующему байту ·переход к следующей 1·pynne Как и во всех случаях с файловыми операциями, MS-DOS может читать последовательные файлы как мето;юм управляющего
диск:ооые нак:опители 365 блока файла, так и методом дескриптора файлов. Только первый из них имеет функцию, специа.,11ьно предназначенную для чтения последовательных файлов. Метод же дескриптора файлов исполь­ зует более общую функцию, манипулируя ею особым образом, требуемым для работы с последовательными файлами. Метод FCB. Функци11 14Н прерывания 21Н чит<1ет последова­ тельные файлы. Надо создать управляющий блок файла и область обмена с диском, как объяснено в {5.3.5 J. Файл должен быть отк­ рыт функцией '0FH прерывания 2lH (см. (5.3.3 ]). DS:DX должны указывать на первый байт FCB, пос.пе чего функция 14Н будет читать при каждом вызове по одной записи из файла. Вы можете установить размер записи по смещению l 4 в FCB. Это надо делать после того, как файл открыт, так как при открытии файла DOS по умолчанию вставля:ет в это полt значение, равное l 28. Каждый раз при вызове функции данные загружаются в память, начиная с первого байта DT А. Если DT А используется как небольшой временный буфер, то перед чтением следующей записи содержимое DTA должно быть перенесено в область данных файла, отведенную в памяти. Можно, наоборот, установить указа­ тель DTА на стартовый адрес памяти, начиная с которого будет размещаться файл, а после чтения каждой записи указатель уве­ личивать на размер записи, с тем чтобы он отмечал то место, где должна быть следующая запись. С помощью установки полей текущей записи (DB, смешение lFH) и блока текущей записи (DW, смещение ОСН), отличными от нуля, последовательный файл может читаться, начиная с любого требуемого места (установка должна быть сделана после открытия FCB). После чтения каждой текущей записи ее поле автоматически увеличивается на 1, а после чтения 128 заткей - увеличивается поле текущего блок.:1. При возврате AL равен О, что означает - вся запись успешно прочитана. При обнаружении конца файла А!~ будет содержать 1, если функция 14Н вообще нс возвра­ тила данных, и 3, если запис1, прочитана частично. В приведенном примере из фай,1а считываются две записи и последовательно помещаются в нужную область памяти. Размер записи установлен равным 256 байтам. Записи считываются в цикле, и после прочтения первой. из них указатель на DT А изме­ няется таким образом, чтобы он был установлен на с.,1едуюший пустой байт в области данных. ;---помещаем FCB в сегмент данных rсв ов DAT ARDB o,· ot.DDATA DAT, 25 DUP(O) 512 DUP С') ;иrпо.11,зуем к,1к ОТ А :---устанамиваем DTA на rшча.,10 об:~асти д;~11111,,х
366 LEA MOV MOV INT DX,DAT_AR DI,DX AH,IAH 21Н ;DS:DX указывают на DTA ;сохраняем копию ;функция установки DTA ;уста11аnлнваем DТЛ ;---открываем файл LEA DX,FCB MOV АН,ОFН INТ 21Н СМР AL,0 JNE OPEN ERROR ;DS:DX указывают на FCB ;функция открытия файла ;открываем файл ;проверка на ошибку ;---устанамиваем размер записи - 256 байт LЕЛ BX,FCB ;DS:DX указI,111ают 11а FCB MOV ЛХ,256 ;размер зашIси Глава 5 MOV DS:[BX] +·14,АХ ;посылаем 11 поле размера зашIси ;---чтение данных MOV СХ,2 NEXТR:MOV АН,14Н LEA DX,FCB INТ 21Н СМР AL,0 JE СОNТ СМР AL,2 JE READ_ERROR СОNТ: ADD DI,256 MOV DX,DI MOV AH,IAH INТ 21Н LOOP NEXTR ;--- закрываем файл LEA DX,FCB MOV AH,I0H INТ 21Н СМР AL,0FH·I JE CLOSE ERROR ;число •штаемых записей ;фу11кци11 ЧTCIНIII файла ;DS:DX указI,111ают 11а l'CB ;читаем од11у заrн-1с1) ;все в порядке? ;проверка 11а ошибку ;уuел1-1•11-11шем указател1. ;DX указI,111ает на 11iщую DТЛ ;фу11кц~111 устаIюI1к~I DТЛ ;устащшшшаем [)ТА ;переход к •псIшю следующей зашIсн ;DS:DX указI,111ают Iш FCB ;фу11к11ня закрытия файла ;закрываем файл ;IIроI1срка на 01ш1бку Метод дескриптора файлов. Функция ЗFН прерывания 21 Н может читат1, данные из файла последоватсл1,но. Эта функция используется для любого чтения из файла с помощью метода деск-
Дисковые накопители 367 риптора файлов, включая файлы прямого доступа. Файл должен быть открыт функцией ЗОН прерывания 21Н с кодом О в AL, если он открывается только для чтения, и с кодом 2, если он открыва­ ется для чтения и записи. При открытии файловый указатель автоматически устанавливается на первый байт файла. Функция чтения из файла указывает, сколько байтов должно быть считано, и после того, как это сделано, файловый указатель устанавлива­ ется на байт, следующий за последним считанным байтом, подго­ тавливая таким образом очередное обращение к функции.· Отме­ тим, что файловый указатель уникален для каждого файла: опера­ ции над другими файлами не меняют его позицию. Программа может создать небольшой временный буфер разме­ ром, скажем, 512 байт, и постоянно вызывать функцию чтения, не заботясь о позиции файлового указателя. Другой метод состоит в считывании всего файла прямо в то место памяти, где он должен быть расположен. В этом случае надо просто потребовать, чтобы функция прочитала больше байтов, чем реально содержится в файле, так как чтение прекращается при достижении последнего байта файла. Однако вам необходимо знать точную длину файла, чтобы знать, где кончаются данные в буфере, в который вы считали файл. Размер файла можно определить, сдвинув файловый указатст, на конец файла. Это надо сделать сразу же после открытия файла. Поместите в AL код 2 и вызовите функцию 42Н, чтобы сдвинут~, указатель на конец файла. СХ и DX должны содержать О, так как в противном случае указатель будет сдвинут с конца файла на величину, которая содержится в этих регистрах. При ·возврате DX:AX будут содержать новую позицию указателя как смещение относительно начала файла, т.е. в данном случае - длину файла. Не забудьте снова вернуть файловый указатель на начало файла, перед тем как читать его; это делается точно так же, с той лишь разницей, что в AL надо поместить О: Если при выполнении функ­ ции 42Н возникает ошибка, то устанавливается флаг переноса, а в АХ возвращается 1, если неверен ~омер функции, и 6, если указан неверный номер файла. , Теперь программа готова для чтения файла. Надо поместить номер файла в ВХ, а требуемое число байтов в СХ и выполнить прерывание. При возврате АХ будет содержать число •реал1,но прочитанных байтов. Если АХ равен нулю, то достигнут конец файла. При других ошибках устанавливается флаг переноса, а АХ содержит 5 при ошибке оборудования, и 6, если указан неверный номер файла. В следующем примере в буфер памяти считывается весь небольшой файл. Для удобства буфер располагается в сег­ менте данных, что существенно увеличивает размер программы на
368 • Глава 5 диске. В своих программах лучше создавать буфер, используя тех­ нику распределения памяти, описанную в [1.3 . l ]. ;---в сегменте данных РАТИ DB 'A:FILENAМE.EXT0 BUFFERDB 1000 DUP (?) НАNDW? SIZE DW ? ;---открываем файл LEA DХ,РАТН MOV AL,0 MOV АН,3DН 1NТ 21Н JC OPEN_ERROR ;строка пути i< файлу ;буфер данных ;номер ф.1йла ;размер файла .;DS:DX указывают на путь ;код открытия для чтения ;функция открытия файла ;открь1ваем файл ;проверка на ошибку . MOV НАN,АХ ;запоминаем номер фаr1ла ;---устанавливаем файловый указатель на конец файла MOV АН,42Н ;функция установки указателя MOV AL,2 ;код конца файла моv моv MOV INT JC BX,HAN сх.о DX,0 21Н POINT_ERRORI ;номер файла ;смещеш1е равно нулю ;устанавливаем указатель ;обработка ошибки MOV SIZE,AX ;запоминаем размер (меньше 64Ю ;---возвращаем указаtель на начало MOV АН,42Н • ;номер функц~ш MOV AL,0 MOV СХ,О • MOV DX,0 INT 2IН JC J>OINT_ERROR2 ;---читаем весь файл MOV АН,ЗFН MOV BX,HAN MOV CX,SIZE LEA DX,BUFFER INT 21Н JC READ_ERROR ;---закрываем фащ MOV ВХ,НАN MOV АН,3ЕН INТ 21Н JC CLOSE_ERROR ;код начала файла ;смещение равно нулю ;устанаwшваем указатель ;обработка· ошибки ;номер фу11кцш1 11теш1я файла ;номер файла ;число с1шtыв.1емых байтов ;DS:DX указывают на буфер ;читаем фаrш ;обработка ошибки ;номер ф.'lйла ;функция закрытия файла ;закрываем ф.1йл ;обработка ош~1бки
Дисковые накопители 369 S.4.S. Запнса. в фанпы прямоrо доступа Физически файлы прямого доступа ничем не отличаются от последовательных файлов, за исключением режима доступа. Фай,, прямого доступа. предполагает, что его данные организов.~ны в виде записей фиксированной длины, таким образом, положение каждой записи может быть вычислено (в последовательных файлах n-й элемент ищется путем подсчета разделителей между элементами, начиная с начала файла). Операционная система автоматически выполняет эти вычисления. Однако любая программа может выполнять эту работу сама, устанавливая файловый указатель на нужную позицию и считывая nоследовательнп т,1~,;ое число байтов, которое о6разуст з:1п11с1,. Высокий уровень В [5.3 .3] объяснен формат открытия файлов прямого доступа в Бейсике. В отличие от последовательноГ<' файла файл nрям('lго дос­ тупа может читаться и ·записываться в одн() и то же время, бс~ закрытия и повторного его открытия. Оператор_ OPEN завсршастС'-: числом, представляющим размер записи файла. Напр11мер, OPEN "R", 1, "NEWDATA",. 20 устанавливает для фаi-iла NEWDATA размер записи в 20 байт (при этом· файл открывается как файл #1). После того как файл открыт, его записи м(,гут быть разбиты на составляющие переменные с помощью оператора FIEI~D . Оператор FIELD указывает, сколько байтов записи отводится пол кажду1" переменную. Например, запись длиной 20 байт может быть раз­ бита оператором FIELD 1, 14 AS LASTNAME$, 2 AS DEPOSП$, 4 AS ACCTNUM$. В этом· операторе п~рвая цифра I указывает, что данный оператор FIELD описывает разбиение записи для файла, открытого под номером # 1. Данные в з.~п·иси располагаются в Т()М порцке, в каком они описаны и операторе FIELD. Операторы RSET и LSET сдвигают данные II полях, выравнивая их по пра­ вому (RSET) или левому (LSET) краю и заполняя остающиеся пустые места пробелами.· Например, для того чтобы вставит,, фамилию "SMITH" в 14-байтовое поле с именем LASTNAME$, надо записать RSET LASTNAME$ = "SMIТH", или если пере­ менной· N$ было присвоено значение "SMIТH", то RSET LASTNAME$ = N$. Вместо RSET может быть испол1,зовано LSET . .Коrда впоследствии данные считываются из поля в переменную, то ей присваиваются все 14 байт. При испол1,зовании RSET про1·­ рамма удалит все лишние пробелы в начале строковой перс-
370 Г,1ава 5 менной, однако, если будет испо,,ьзоваться LSET, пробелы бу;~ут уда,,яться справа. Отметим,· что все имена переменных в операторе FIELD отно­ сятся к строковым переменным. В файлах прямого доступа Бейсик рассматривает все переменные, включая числовые, как строковые. Чисдовая переменная до,1жна быть преобразована к специальному виду, прежде чем ее значение может быть присвоено полю, а когда она затем считывается из поля, необходимо обратное преобразование. Слово преобразование стоило бы заключить в кавычки, поскольку Бейсик на самом деле не меняет способ представления числа в памяти; он просто обрабатывает число особым образом. Числовые поля требуют два байта для целых чисел, четыре байта для чисел с обычной точностью и восемь байтов для чисел с двойной точностью. Такое же число байт требуется и для представления этих чисел в памяти. Для преобразования их в строковую форму надо использовать функции МК1$, MKS$ и MKD$, которые осуществляют преобразование число-строка для целых, вещественных и чисе,1 с двойной точностью соответственно. Обычно эти функции комбинируются с операторами RSET или LSET, например RSET = ACCTNUM$ = МК1$(Х), где Х - целая переменная, если полю ACCTNUM$ было отведено два байта в операторе FIELD. После того как· поля заполнены операторами RSET и LSET, запись выводится на диск с помощью оператора PUT #. PUT # 1, 245 помещает данные в запись номер 245 файла, открытого под номером # 1. Номер записи может быть опущен, в этом случае данные помещаются в запись с номером на единицу больше, чем номер последней .записи (начиная с записи 1). Запись выводится вся целиком, даже если не все поля были заполнены данными. Отметим, что поля _буфера не очищаются при выполнении опера­ ции PUT, поэтому элементы данных, например текущая дата, могут помещаться в буфер только один раз, а затем они будут помещены во все записи, которые будут выводиться в течение дан­ ного сеанса работы. Функция LOC возвращает номер последней выведенной в файл записи. Если файл был открыт под номером #3, напишите Х = LOC(3). Функция LOF (длина файла) возвращает длину файла в байтах. Для определения числа записей, содержащихся в файле, надо разделить это значение на длину записи. Добавление 1 к этому значению дает номер записи, который надо использовать, чтобы добавить к файлу новые записи. Если файл был открыт под номером #2, а длина его записей равна 32 байтам, то требуемо1.; значение вычисляется как RECORDNUM = LOF(2)/32 + 1. В следующем примере файл прямого доступа открывается с· длиной записи, равной 24 байтам, причем запись разбита на три
дис1еовые накопители 371 " переменные. Программа запрашивает •пользователя о содержи мом всех трех полей, а когда все они введены, добавляет запись к файлу. В строке 120 вычисляется начальный номер записи. Отме­ тим, что каждый раз при выполнении оператора PUT данные могут не записываться физически на диск. В выходном буфере могут накапливаться несколько записей и только затем выводиться на диск. • 100 OPEN "R ", l, "A:NEWDATA.DAT", 24 'открыв.'lем файл 110 FIELD l, 18 AS LASTNAME$, 2 AS AGE$, 4 AS WEIGHT 120 R = LOF(l)/24 + 1 130 CLS 140 INPUT "Enter name:",N$ 150 INPUT "Enter age:",A% 160 INPUT "Enter weight:",W! 17,0 RSET LASTNAME$ = N$ 180 RSET AGE$ = МК1$(А %) 190 RSET WEIGHT$ = MKS$(W!) 200 PUT #1, R 210R=.R+1 220 PRINT: PRINT "Do another (y/n)?" 230С$=INKEY$:IFС$= "" THEN220 240 IF С$ = "у" ТНЕN CLS: GOTO 130 250 CLOSE Средний уровень ·номер посJ1ед11ей заш1сн + 1 чистим экран ·поJJучаем ~•мя (строка) ·получаем возраст (целое) ·получаем вес (веществешюе) помещаем в поле имя ·помещаем в поле возраст ·помещаем в поле вес выводим зап~1сь ·увеличиваем счет•шк ·запрос пользователя •ожидаем ответа ·если да. то на начало ·иначе - закр1о1ваем ф.'lйл "·MI ·' Как и при всех других операциях .с файлами, в MS-DOS' ЙМ_t;- ются два метода записи в файл прямого доступа: один - с исполь­ зованием управляющего блока файла, а другой - с помощью деск­ риптора файла. В обоих случаях вы должны создать буфер обмена данными, размер которого должен быть не меньше чем размер записи. Метод управляющего блока файла. Откройте управляющий блок файла с помощью функции 0FH и пусть DS:DX указывают на него. После того как файл открыт, поместите номер записи для прямо~ доступа в поле записи прямого доступа FCB. Затем вызо­ вите функцию 22Н прерыван~я 21Н, которая передаст данные из DTА в файловый буфер, организованный при создании FCB. Данные могут быть записаны на диск не сразу, если размер записи меньше размера буфера. Реальная запись на диск будет проис,-
372 Г.лава 5 ходить то~а, когда очередной вызов функции 22Н заполнит буфер. При возврате из функции 22Н AL будет содержать 00, если обмен проше.,'1 успешно. В противном случае в нем будет нахо­ диться l, если не хватает пространства на диске, и 2, если область переноса MЗJfa дт1 roro, чтобы вывести одну запись (т.е. если раз­ мер буфера, устанОВJ1еиный: системой, меньше, чем тот, который у:казан в FCB). ;---в сеп.еите данных FCB DB 1, 'NEWDATA •. 25 DUP (0) DTADB ZS6 DUP ('?) ;---оrкрwваем файл и устанаw~иваем пол• FCB MOV AH,OFH ;номер функции LEA DX,FCB ;DS:DX указывают на FCB MOV ВХ,DХ ;копируем смещение дл ■ FCB INТ 21Н ;открываем файл MOV АХ,256 MOV {ВХ) + 14,АХ MOV АХ,233 MOV [ВХ] + 33.АХ MOV АХ.О MOV [ВХ] + 35,АХ ;размер записи ;помещаем в поле размера записи ;номер записи ;помещаем в поле номера записи ;обнуляем старший байт этого слооа ;---перенос данных из DTA в файл MOV АН,22Н ;номер фушщии записи с прямым доступом LEA DX,FCB ;DS:DX указывают на FCB INТ 21Н ;запИСhlваем данные СМР AL,0 ;проверка н;1 ошибку JNE WRПE_ ERROR ;---закрываем файл LEA DX,FCB ;DS:DX указывают на FCB MOV АН,lОН ;функция 31UСрыти• файла INТ 21Н ;закрываем ф.-~йл СЫР AL.0FFH ;проверка на ошибку JE CLOSE_ERROR Часто программа работает сразу с несколькими записs~ми пvя­ моrо доступа, передавая их в память и из памs~ти как единое целое. При использовании метода FCB MS-00S предостаuяет для этоrо специальную функцию, н~зываемую запмсь 6.лока с п,н1.мы.м доступом. Это функция 28Н прерывания 21 Н. При входе DS:DX должны: указывать на открытый FCB, в котором поле записи пря-. моrо доступа должно быть равно номеру первой из выводимых записей. Эта функция совершенно аналогична функции в приве­ денном выше примере. Единственное отличие (кроме номера
ДистсОбЫе натсопите.ли 373 функции) состоит в том, что в СХ должно быть указано число записей в блоке (не путайте эти "блоки" с блоками по 128 запи­ сей, с помощью которых систем-а находит требуемую запись - программа может читать любое число записей, начиная с любого места). В СХ возвращается число реально прочитанных записей. AL будет содержать О, если все записи успешно выведены, l, ес.,,и не хватает пространства на диске (при этом не будет вы11едена ни одна запись). В отличие от 22Н эт.1 фvнкция автоматически увеличивает поля текущей записи, текущего блока и записи прямого доступа в FCB, так что они будут указывать на запись, следующую за последней прочитанной. Отметим, что если при въrполнении этой функции сделать СХ = О, размер ф.1йда будет установлен в соответствии с числом записей, равным IIO.,'I IIO записи прям:оn, доступа. Таким образом можно резервировать дисковое пространство для файла. ' Метод дескриптора файлоо. При использовании для доступа метода дескриптора файлов система не различает последо­ вательные файлы и файлы прямого доступа. Ваша программа должна вычислить позицию в файле, с которой начинается требу­ емая запись, и установить на нее файловый указатель. Файловый указатель позиционируется с помощью функции 42Н прерывания 21Н. Поместите номер файла в ВХ, а смещение в файле - в CX:DX (СХ будет содержать старший байт значения). Затем поместите в AL кодовый номер от О до 2. При AL = О указатель будет установлен со смещением CX;DX байтов относительно начала файла; при AL = 1 указатель будет установлен со смеще­ нием CX:DX относительно текущей позиции, а при AL = 2 указа­ тель будет установлен со смещением CX:DX относительно конца файла (т.е. таким образом файл будет расширен). Отрицатс.,1ьные числа недопустимы в качестве смещений. При возврате DX:AX будут содержать новое положение указателя (старший байт в ОХ). Если устанавливае'ГС51 флаг переноса, то •произошла ошибка. В этом случае АХ будет оодержать 1, если указан неверный код в AL, и 6, если указан неверный номер файла. После позиционирования фай:,1овоrо указателя з.·шись прямого доступа· выводите~ с помощью той же функции 40Н прерывания 21 Н, которая исnо.льзова.m~сь для вывода в пос.;~едов.ателъный файл. Пр~: входе ВХ содержит номер файла, а СХ - чиСJJо байтов, кото­ рое надо записать. При возврате АХ будет содержать число реально записанных байтов. Если оно отличается от числа, rюме­ щенноrо в СХ, то, вероятно, диск полон (см. [5.1 .4 }). Как обычно, при возникновении ошибки устанаuивается флаг переноса. В этом с1учае АХ будет содержать 5 при ошибке накоnитсля и 6, если указан неверный номер файла.
374 Глава 5 Файловый указатель играет ту же роль для образа файла на диске, что и DTА для образа файла в памяти. Он может сдви­ гаться как уrодно для доступа к различным частям файла. Будьте внимательны, манипулируя файловым указателем при работе с файлом прямоrо доступа - содержимое любого поля любой записи может быть легко извлечено с диска и помещено в требуемое место в памяти. ;---в сегменте данных НАNDW? РАТИ DB 'A:NEWDATA',0 BUFER DB 30 DUP (?) ;---открываем файл MOV АН,ЗDН MOV AL,l LEA DX,PATH lNT 21Н JC OPEN ERROR MOV HAN,AX ;номер файла , ;строка пути к файлу ;буфер выводимых заш1сей ;номер функции ;код открытия для записи ;DS:DX указывают на путь ;открываем файл ;проuерка на 011шбку ;сохраняем 1юмер файла ;---вычисляем позицию записи и устанавливаем файловый указатель MOV АХ,30 MOV СХ,54 MUL СХ MOV CX,DX MOV DX,AX ;размер записи 30 байт ;номер записи .#54 <55-я заш1сь) ;теперь смещеш1е для 11ее n DХ:Л).( ;помещаем старшее CЛOIIO 11 DX ;помещаем младшее слшю 11 СХ MOV AL,0 ;устанаплш~аем указател1, 11а Ш1'1а;ю MOV ЛН,42Н ;функции установки указателя MOV BX,HAN ;номер файла lNT 21Н ;уста1ш11шшаем указатст, JC POINTER ERROR ;проверка на Оlш1бку ;---выводим запись с прямым доступом MOV АН,40Н ;номер функции MOV ВХ,НЛN MOV СХ,30 LEA DX,BUFER INT 21Н JC WRIТE_ERROR ;номер файла ;размер записи ;DS:DX указьшают на буфер ;выводим запись ;проверка 11а ош~1бку В отличие от метода FCB метод дескриптора файлов нс предос­ тавляет специальной функции для вывода блока записей прямого
дисковые накопители 315 доступа. Однако вашей программе необходимо только вычислить количество составляющих блок записей байтов, которое должно быть выведено. 5.4 .6. Чтение нз файлов nрямоrо доступа Чтение из файлов прямого доступа является обратным процес­ сом по отношению к их выводу. MS-DOS вычисляет позицию в файле на диске, затем считывает запись и помещает ее в .,:1мяп. Дадее программа должна разделить запись на поля того же раз­ мера, который был использован при конструировании записи. Не забудьте удалить символы пробела, добавленные при заполнении полей. Обсуждение процесса вывода данных в файлы прямого дос­ тупа (см. [5.4 .5 ]) поможет вам лучше понять содержание данного параграфа. Высокий уровень Для чтения файла прямого доступа необходимо открыть его и определить подя записи, как объяснено в параграфе, относящемся к записи в файлы прямого доступа. Затем надо использовать оператор GET# для. чтения опр.еделенной записи с диска. GET #1,23 считывает ;~апись номер #23 из файла, открытого под номером #1. При чтении записи переменной, именованной в операторе FIELD, автоматически присваивается соответствующее значение из записи. Например, если оператор FIELD имеет вид FIELD 1, 20 AS Х$, 2 AS У$, то после выполнения оператора G ЕТ 1,23 переменной Х$ будет присвоено значение первых 20 байт записи 23; а переменной У$ - следующих I О байт. Операторы, аналог.ичные RSET и LSET, применяемые для выде.,~ения полей данных, отсутствуют. В случае числовых полей напоминаем, что они должны быть преобразованы в строковый вид с помощью функций MKI$, MKS$ и MKD$. Для восстановления их оригинальных значений, с тем чтобы над ними можно было проводить операции и печатать их, надо преобразовать эти строки с помощью функций CVI, CVS и CVD. Если У$ содержит целое число, то для выполнения обратного преобразования запишите У% = CVI<Y$), при этом переменная У% будет содержать значение, которое она имела перед тем, как была специально обработана для вывода в файл прямого доступа. Если вы выведете строковое значение переменной, то увидите, что это число в интервале от О до 65535, зако;:хированное в два символа ASCII.
376 Глава 5 В следующем примере открывается файл, созданный в примере из (5.4.5 ], и выводится содержимое любой из затребованных записей. 100 OPEN "A:NEWDATA" AS #1 LEN = 24 ·открыщн:м файл 110 FIEШ l, 18 AS LASTNAME$, 2 AS AGE$, 4 AS WEIGHT$ 120 CLS: INPUT "What is the record numЬer";R ·запрос записи 130 IF R*24 > LOFO) THEN ВЕЕР: PRINT"No s11ch record": GOTO 120 140 GET #1,R ·•штаем запись из файла 150 PRINT LASTNAME$, CVl(AGE$), CVS<WEIGHT$) ·вывод~1м ее 160 PRINТ: PRINT "Do another (у/п)?" 170С$ =INКEY$:IFС$ = ""ТНЕN170 180IFС$="у"ORС$="У"THEN120 190 CLOSE Средний уровень 'будем повторять? •ожидаем ввода ·повторяем при необходимости ·ина•1е - закрываем файл Метод FCB доступа к файлам имеет две функции для чтения записей с прямым доступом. Метод же дескриптора файлов использует ту же функцию, что и для чтения последовательных файлов. Каждый из методов доступа будет рассматриваться отдельно. , Метод •FCB. Функция 21 Н прерывания ·2 l Н читает по одной записи из файла прямоrо доступа, в то время как вторая функция, 27Н, может читать блок последовательных записей. Создайте _управляющий блок файла, как показано в [5.3.51 и откройте ero '(см. [5.3 .3)). После тоrо как FCB открыт, введите в него значения :Д<;>д~Й размера записи (DW по смещению 14) и номе·ра записи прямоrо доступа (DD по смещению 33). Если DS:DX указывают на первый байт FCB, то можно вызывать функцию 21 Н для чтения записи, которая будет помещена в память,· начиная с первого байта DTA.• Если запись успешно прочитана, то в AL будет возвращен О. Однако при этом нет гарантии, что чтение прошло без ошибок, поскольку неверный размер записи может привести к тому, что части прилегающих записей будут считаны, как будто это одна запись. Если запрошена запись с номером большим, чем число записей в файле, то в AL будет возвращено l или 3. Возвращение кода 3 означает, что был считан самый конец файла и была про­ читана часть записи данных, возвращение кода l - что данные вообще не были считаны. В следующем примере считывается одна запись и помещается в DTA:
ДискQвые накопители ;---в сегменте данных FCB DB 1,'OLDDATA , 25 DUP (О) ;---открываем·файл ~t устававливаем ло,,я FCH MOV AH,0FH ;1юмер функции LEA DX,FCB. ;DS:DX указь111,,ют на FCH MOV BX,DX INT 21Н ;копируем смещение FCB ;открываем файл MOV АХ,55 . ;размер заш ю1 55 байт . MOV [ВХ] + 14,АХ ;помещаем в поле размера заш1си MOV АХ,22 ;номер записи д;1я •певия MOV [ВХ] + 33,А:'( • ;помещаем в поле ,юмер.'I з.,ш1си MOV АХ,О ;обнуляем старшее слово этого поля MOV [ВХ) + 35,АХ ;---перенос данных из файла в DTA MOV АН,21Н ;номер функции. •пения с прямым доступом LEA DX,FCB ;DS:DX указывают.11а FCB INT 21Н СМР AL,0 ;читаем данные, помещая их 11 DTA ;проверка ,ш ош~1бку ,JNE READ ERROR ;---закрываем файл MOV АН,10 LEA DX,FCB INT 21Н ;номер функцш1 закрытия файла ;DS:DX указывают 1111 FCH ;закрываем файл 377 Для чтения блока последовательных записей в памят.ь 4~ oJiи~ прием применяют функцию 27Н прерывания 2\Н. Ее выпо.гi11.с,нfr'е подготавливается точно так же, как и функции 21 Н, за исrl.ЖЬч/!-­ нием того, что СХ должен еще содержать число записей, кof'o'JR,fe надо прочитать за од~ прием. При возврате СХ будет содержат~, число реально прочитанных записей. Значения возвращаемые ,в AL совпадают с теми, которые возвращаются функцией 2\Н. В отли­ чие от функции 21-Н поля FCB, в которых хранится информация о положении записи (поле записц прямого доступа, текущего блока и текущей записи), автоматически увеличиваются, с тем чтобы они после выполнения функции указывали на следующую несчи­ танную запись. Отметим, что как в случае чтения одной, так и в случае чте­ ния несколькqх записей поля текущего блока и текущей записи FCB устанавливаются по значению поля записи прямого доступа. Если вам известны значения текущего блока и текущей записи, а не соответствующий номер зl\писи прямого доступа, то исполь-
378 Глава 5 зуйте функцию 24Н прерывания 21 Н, чтобы она проделала за вас все вычисления. У этой функции нет входных регистров; надо только, чтобы DS:DX указывали на открытый FCB. При возврате поле записи прямого доступа будет заполнено значением, соответ­ ствующим установке двух других полей. Метод дескриптора файлов. Выше было показано, как выводить записи прямого доступа с помощью метода дескриптора файлов. Процедура чтения из файла с прямым доступом подrотавливаетсs~ аналогичным образом: путем вычисления смещения в файле, на которое должен быть установлен файловый указатель. DS:DX должны указывать на буфер, в который будет помещена запис1,, после чего надо выполнить функцию ЗFН прерывания 21Н. При входе СХ должен содержать размер записи, а ВХ - номер файла. ;---в сегменте данных HAND8? РАТИ D8 A:OLOOATA ,О BUFER D8 30 OUP(?) ;---открываем файл MOV АН.ЗОН ;номер функции MOV AL.0 ;код открытия ддя •1теннs1 LEA ОХ.РАТИ ;OS:OX указывают на пуп, к файлу INT 21Н ;открываем файд JC OPEN_ERR ;проверка 1ш 011111бку MOV HAN,AX ;запоминаем номер файда ;---вычисляем позицию записи и устанавшшаем файдо11ый указатедь MOV АХ,30 MOV СХ,54 MUL СХ MOV CX,DX MOV ОХ,АХ MOV AL,0 MOV АН,42Н MOV BX,HAN ;размер записи ;читаем запись #54 (55-ю запись) ;смещение запис11 в DX:AX ;помещаем старшее сдопtсмещеш1я о DX ;помещаем мдадшее CJIODO смещеш1я о СХ ;устанам1111аем указатею, на 11ачаJю файла ;фу11кция усп11ювки указнтедя ;номер фнйда INT 21Н ;устанам1шаем указатедь JC POINT ERR ;обработка ошибки ;---читаем запись с прямым доступом MOV АН,ЗFН ;номер функции MOV ВХ,НАN MOV СХ,30 LEA OX,BUFER ;номер файла ;размер записи ;DS:OX указьшнют нн Сlуфер для заш1си
Дисковые накопители 379 \INT 21Н ;читаем запись JC READ ERR ;обработка ошибки ;---закрываем файл MOV .BX,HAN ;номер файла MOV АН,ЗЕН ;функция закрытия файла INT 21Н ;закрываем файл JC CLOSE_ERR ;проверка на ош~1бку S.4 .7 . Проверка _данных nocne операций чтення/запнсн MS-DOS может проверять правильность производимого обмена с диском непосредственно во время обмена. Ошибки происходят настолько редко, что средства проверки обычно не используются, чтобы не замедлять обмен с диском. Однако в случае необходи­ мости имеются два способа проверки. Один состоит во включении команды VERIFY = ON в файл CONFIG.SYS, который автома­ тически читается при загрузке операционной системы. Впослед­ ствии все дисковые операции будут проверяться. Это единственный способ проверки, доступный в Бейсике. Второй метод состоит в ис­ пользовании специальной функции DOS для вериф1,i'кации только критических дисковых операций. Если процедура верификации обнаруживает ошибку, она- сообщает об условии критической ошибки, как описано в {7.2 .5 ]. Средний уровень Функция 2ЕН прерывания 21Н включает и выключает про­ верку. Для включения верификации поместите в AL 1, для выклю­ чения - О. DL также должен быть равен О. Затем надо выполнить прерывание. У функции нет выходных регистров. ;---включение верификации MOV AL, 1 ;номер кода MOV DL,0 ;необходимый входной регистр MOV АН,2ЕН ;номер функцш1 INT 21Н ;включаем проверку Для определения текущего режима верификации надо вызвать функцию 54Н прерывания 21Н. У нее нет входных регистров. При возврате AL = 1, если проверка включена, и AL = О, если выключена.
380 Глава 5 5.4 .8 . Определение дисковых ошибок и восстановление после них Дисковые операции очень сложны, поэтому возможно большое количество ошибок. Больши,нство дисковых ошибок обсуждается вместе с операциями, при которых они могут возникать. Здесь же они собраны вместе, чтобы ломочь вам при разработке процедуры общего назначения для восстановления после дисковых ошибок. Дисковые ошибки бывают двух типов: так называемые мягкие (soft) и жесткие (hard). Мягкие ошибки возникают из-за непра­ вильного запроса на доступ к файлу (запрошенный файл может отсутствовать или дисковое пространство может кончиться прежде, чем будет записан весь файл). Жесткие ошибки возникают из-за неверного порядка или временных несоответствий при дисковых операциях, которые могут быть следствием неправильного вырав­ нивания или проблем с накопителем. В этом случае лучше всего произвести сброс диска перед обработкой. Высокий уровень В [7.2.5} объяснено, как подготовить процедуру обработки ошибок. Оператор ON ERROR GOSUB заставляет программу перейти на процедуру обработки ошибки при возни~новении кри­ тической ошибки. Процедура прежде всего определяет кодовый но­ мер ошибки в Бейсике. Ниже перечислены коды дисковых ошибок: 52 Bad Jile питЬеr. (Неверный номер файла.) Файл открыт не rюд тем 1юме­ ром. к которому идет обращение (#1, #2 и т.д.)., 53 File ,wt found. (Фаitл не найден.) Исполь3уется при выполнеш1и опера­ торов LOAD, КILL, NAME, FILES и OPEN. 54 Bad file mode. (Неверный режим доступа.) Попытка доступа к файлу, открытому с другой целью, например попытка 3аписи в последовательный файл, открытый для чтения. 55 File already ореп. (Файл уже открыт.) Попытка отк·рыть файл, который уже открыт, или уничтожить (KILL) файл, который еще не закрыт. 58 File already exists. (Файл уже существует.) Попытка переименован, файл (с помощью NАМЕ) на имя, которое уже есть в катало1·е. 61 Disk full. (Диск полон.) См. специальное обсуждение 11 15 .1 .4\. опюся11tе­ еся к этой ошибке. 62 lnput past end. (Чтение за концом файт1.) Попытка прочитат1, из 1юс:1е­ довательного файла больше переменных, чем он содержит. Чтобы избе­ жать этой ошибки, исполЬ3уйте функцию EOF, как объяснено в [5.4.4], 63 Bad record numher. (Неверный номер 3аписи.) Попытка прочитать или вывести запись с номером большим, чем число записей в фай.~е.
Дис,совы.е на,сопители 381 64 Вшi file пате. (Неверное имя файла.) Используется операторами КILJ,, NAME и FILES. 67 Тоо тапу files. (Слишком много файлоо.) В каталоге бо,1ьше нет места для записи информации о файлах. Друrой возможный вариант состоит в том, что открытие еще одноrо файла приведет к тому, что будет превы­ шено максимально допустимое чиСJЮ од~ювременно открытых файлои. 70 Disk is write-protected. (Диск защищен от записи.) 71 Disk is n.ot reшiy. (Диск не rотои.) Наибо·,1ее вероятно, не закрыт дискоuод с дискетой. 72 Disk medi.a e"or. (Диск поврежден.) Как правило, это сообщеш1е 111,1:щется при повреждении дискеты, однако иногда оно поя11Ляется 11ри сбою, обору­ дования. 74 Specified wrong disk in RENAME operation. (Указан 11евер11ый днск II опе­ рации RENAМE.) 15 Path/file access ~or . (Ошибка доступа к файлу.) Попытка откр1,ап, под­ каталог или метку тома как файл. Или Ш)пытк:~ ш1сать в файл, который защищен от записи. Эта @шибка чаще всею выдается при попа..п"е уда­ лить текущий каталог. Появ.п·яется при операциях OPEN, NAME, MKOIR , CHDIR и RMDIR. 76 Path ,wt found. (Путь не найден.) Непраашлыю указа11 пуп, н:ш е1·0 нt: существует. Появляется при операц~LЯх OPEN, MKDfR , CHDIR н R\-l[)IR. После того как процедура распознала ошибку, нсобхn.;щмо информировать об ошибке пользователя. Когда по.,1ьзов.1тель ..:ооб­ щит, что причина ошибки устранена, оператор RESUME вuзвра­ щает программу назад на ту строку, где произошла ошибка. Опе­ ратор RESUME может сопровождаться номером строки, по:лому программа может вернуться к началу всей последовательности дис­ ковых операций, независимо от тоrо, в какой строке произошла ошибка (отметим, что файлы не закрываются при возникновении ошибки). В следующем примере программа позволяет восстановить ситуацию после ошибок, свяЗ.1нных с переполнением диска 111 защитой от записи: 100 ON ERROR GOSUB 5000 ·,1клю'lt:11ие обработки 01шrбок 600 • ·здесь начинаются дисковые onepaцf1f1 5000 • ·подпрограмма обработки ошибок 5010 IF ERR 61 PRINT "Disk fiall .. : GOTO 5100 5020 IF ERR = 70 PRINT "Disk is write p1·otccted": GOTO 5100
382 5100 PRINT "Correct the proЫem, then strike any key" 5110 С$ = INКEY$: IF С$ = "" THEN 5110 5120 RESUME 600 Средний уровень • Глава 5 Функция l прерывания lЗН возвращает в AL байт, сообща­ ющий.., статус дисковоrо накопителя. Значения ero битов могут быть следующими: биты 0-1 01 = неверная команда, или, если бит 3 данными за границей 64К 1О = адресная метка не найдена 1, то попытка обмена 11 = попытка записи на защищенный от записи диск 2 3 указанный сектор не найден переполнение DМА (потеря данных при обмене), ~1щ1, если бит О = 1, попытка обмена дан11ыми за границей 64К 4 данные прочитаны невер110, надо nовтор~1ть 5 ошибка контроллера 6 = ошибка операции поиска 7 нет ответа от накопителя (тайм-аут) Каждая из функций обращения к диску MS-DOS возвращает только часть из возможных кодов ошибок, а некоторые функции не сообщают об ошибке. Однако во всех случаях при возникно­ вении ошибки устанавливается флаг переноса. Если произошла ошибка, то номер кода этой ошибки возвращается в АХ. Ниже перечислены коды, относящиеся к дисковым операциям: Неверный номер функции 2 Файл не найден 3 Путь не найден 4 Уже открыто максимально допустимое •шсло файлов 5 Отрицание доступа (ошибка оборудоваш111) 6 Неверный номер файла 15 Указан невер11ый 11акошпель 16 Попытка удалить текущий каталог 17 Не то устройство 18 Больше нет файлов (при поиске в каталоге с ис1ю111,зо1111ш1см "джокеро11")
Дирковые накопител.и 383 Восстановление после этих "мягких" ошибок не сложно. Неко­ торые из них предупреждают ва_с о программных ошибках. Другие возникают из-за ошибочных действий пользователя. Если же не отвечает сам накопитель, то произошла критическая ошибка. В (7 .2.5 ] показано, как написать процедуру обработки критических ошибок. В MS-DOS З.О введены расширенные коды ошибок. Они могут быть получеl(ы с помощью функции 59Н прерывания 21Н, когда флаг переноса индицирует возникновение ошибки. Обсуждение этого вопроса см. в (7.2 .5 ].
Глава 6. Принтер 6.1. У nравленне работой принтера MS-DOS может работать с тремя парал­ лельными устройств.~ ми (LPT l - LРТЗ), и в этой главе показано, как ущх1щ1ять ими. Пос.ледuва1'ельные принтеры управляются так же, как и пщх1лд1,;.1ыiьн~, за иск:1ючением способа, с помощью которого даtJныс ш.х:1,1)шк,то1 tt.:t принтер; эта информация приве­ дена в р~здеде I г.Jшны 7. Каждое параЛJiельное устройство имеет свой адаптер. Адантер управляется тремя регистрами • ввода/ вывода; адреса портов этих , регистров различны для каждого адаптера. Область данных BIOS содержит базовые адреса для каждого адаптера. Базовый адрес соответствует младшему адресу группы из тр~х адресов портов. Базовый адрес для LPTl находится в ячса~.;.t:; 004U:0008, для LPT2 - 0040:ОООА и т.д. Как видно и~ привсд~нной. ниже таблицы, какой адаптер назначен какому номеру LPT не определено. По этой причине программа, которая прямо адресуется в параллельный порт, должна выиски­ вать используемые им адреса. Отметим, что при инициализации базовому адресу присваивается значение О, когда соответствующий адаптер не установлен. Регистр Регистр Адаптер "ь1х.:,дных данных статуса Монохромная карта (РС/ХТ/АТ) Адаптер принтера Р~/ХТ Адаптер принтера· PCJr Последовательная/параллельная карта АТ (уст11новленная как LPT 11 ЗВСН ЗВDН 378Н 379Н Perиc·rp управления ЗВЕН 37АН
Принтер Последовательная/параллельная карта АТ (установпенная как LPT2) 278Н 385 279Н • 27ЛН Регистр выходных данных - тот адрес порта, через который п}Юходит каждый байт данных, посылаемый. в принтер. Регистр статуса сообщает различную информацию о принтере; . процессор может постоянно опрашивать ero, чтобы распознать момент, когда все в порядке и можно посылать данные. Регистр статуса сообщает также, что произошла ошибка на принтере. Регистр управления инициализирует адаптер и управляет выво~ом данных. Он может также подrотавливать параллельный порт для операций преры­ вания, с тем чтобы принтер посылал прерывание к nJioцeccopy, когда он rотов к приему очередноrо символа, •оставляя процесоор свободным для других дел. Ниже перечислены значения битов регистров статуса и уцравления: Реrистр упрамения бит О О нормальная установка, l О нормальная уста11овка, ! возврата каретки вызывает вывод байта данных автоматический перевод строки после 2 О = инициализировать порт принтера, L = нормальная установка 3 О = отмена выбора принтера, J = нормальная ·установка 4 О = прерывание принтера запрещено, l = разрешено S- 7 не используются Регистр статуса бит 0-2 не используются 3 О ошибка принтера, l = нет ошибки 4 принтер off-line, l = принтер on-line о= о 5 бумага вставпена, l = нет бумаги 6о о =. = принтер подтверждает прием символа, 1 принтер занят, 1 = принтер свободен нормальная установка Не может быть никаких оснований для тоrо, чтобы в п}Юг­ рамме отсутствовала процедура восстановления после ошибок, воз­ никающих при работе с принтером. Грамотно написанная прог­ рамма должна начинать работу с проверкR тоrо, что принтер rотов к работе с машиной (on-line)•. Если присоединен не один принтер, то программа должна предложить пользователю выб_рать, с каким * . . Английский термин on-line подразумевает, что устройство готово обмениваться информацией с машиной. В настоящей книге для обозначения этого факта мы будем пользоваться термином связан с машиной. - Примеч. пер. 13 Р. ДжордеАн
386 Глава 6 из них он будет работать. Кроме того, процедура печати должна восстанавливать ситуацию при любых ошибках принтера, при этом хотелось бы, чтобы не было необходимости снова печатать весь документ. 6.1 .1. Инициализация порта принтера/повторная инициализация принтера Программы должны инициализировать порт каждого принтера (LPT l - LРТЗ) перед первым его использованием, а также повтор­ но инициализировать после устранения причин ошибки принтера. Не путайте инициализацию порта принтера с инициализацией самого принтера. Инициализация принтера это внутреннее дело принтера. Она происходит автоматически при его включении и в большинстве случаев принтер не может быть повторно инициали­ зирован без его выключения и повторного включения. Но прог­ рамма может повторно инициализировать включенный принтер, т.е. могут быть восстановлены начальные параметры работы прин­ тера, отменяя все специальные шрифты, остановы табуляции и т.д. Считается правилом хорошего тона производить такой сброс прин­ тера, после того как программа завершает работу с ним. Языки высокого уровня инициализируют порт принтера авто­ матически, но программы на языке ассемблера требуют для этой цели короткой процедуры. Однако восстановление начальных параметров печати требуется во всех программах. Некоторые принтеры, такие, как новые эпсоновские принтеры, имеют "главный код сброса", который приводит к полному сбросу прин­ тера. Но поскольку не все принтеры имеют такой код, программа должна предусматривать в своей завершающей части восстановле­ ние всех измененных параметров. Например, она может подать ко­ ды выключения курсива, плотной печати и т.д. Не забудьте вклю­ чить вызов этой процедуры в процедуру выхода по <<Ctrl-Break». Помните, что на многих принтерах символы не печатаются до тех пор, пока не получен крд возврата каретки, завершающий строку (или до тех пор, пока не введена целая строка данных). Символы могут спокойно ожидать в буфере принтера, даже после того, как породившая их программа завершилась. Когда начина­ ет<;я новая передача данных на принтер, эти символы будут напе­ чатаны. Чтобы избежать этой проблемы, не забывайте почистить буфер перед началом печати; а также возьмите себе за правило чистить буфер при завершении программы. Это делается посылкой на принтер кода ASCI1 24 (при этом параметры печати не меняются).
Принтер 387 Средний уровень Функция 1 прерывания 17Н ВIOS инициализирует порт прин­ тера и возвращает байт, выдающий статус порта. Поместите в DX номер порта - число от О до 2 для LPTl - LРТЗ, после чеrо вызо­ вите прерывание. Байт статуса принтера (идентичный обсужда­ емому в (6.1 .2)) возвращается в АН. ;---инициализаци11 LPTI MOV АН,! ;функци11 инициализации принтера MOV DX,0 ;LPТl INT 17Н ;проводим инициализацию Низкий уровень Регистр управления выводом каждоrо адаптера принтера имеет бит, который вызывает инициализацию адаптера. Этот регистр имеет адрес порта на 2 больше, чем базовый адрес адаптера. Напо­ минаем, что базовый адрес для LPT 1 хранится в . ячейке 0040:0008, для LPT2 - в 0040:ОООА и т.д. Имеют значение только младшие 5 бит регистра управления выводом. Бит 2 - бит иници­ ализации принтера и обычно он устанавливается в 1. Для иници­ ализации адаптера надо сбросить этот бит в О на тысячу тактов пустого цикла (3000 для АТ или на 1/ 20 секунды, если испо,1Iь­ зуется счетчик времени суток BIOS (2.1 .5 ]) . В этот момент нужно, чтобы был установлен только бит 3 (принтер выбран). Поэтому пошлите в порт значение 12, сделайте задержку, а затем пошлите в порт обычное (без прерываний) инициализирующее значение, равное 8. • 13* В следующем примере инициализируется LPTl: ;---инициализируем LPTI мqv DX,ES: (8] ;считываем базовый адрес в DX INC DX ;прибавляем 2 к базовому адресу INC DX MOV AL,12 OUT DX,AL DELAY: MOV АХ,1000 DEC АХ JNZ DELAY MOV AL,8 OUT DX,AL ;значение для инициализации ;начинаем инициализацию ;начало nycтoro цикла ;уменьшаем счетчик ;повтор11ем 1000 раз ;обычное значение для регистра ;конец инициализации
388 Глава 6 6.1.1. Проверка тоrо, что прннтер связан с маwнной Перед тем как послать на неrо вывод, программа всегда должна проверить, что принтер связан с машиной. Легко выявить, что принтер не rотов, так как бит 3 регистра статуса принтера в этом случае устанавливается в 1. Но намноrо сложнее точно опреде­ лить, почему принтер не rотов: выключен ли он, отменен ли выбор принтера или в нем нет бумаm. Это происходит из-за тоrо, что принтеры разных производителей посылают разные наборы битов в регистр статуса принтера, даже когда они находятся в идентичном состоянии. Хотя реmстр статуса имеет биты, которые должны показыв~ть все три состояния принтера, но в реальности значения битов могут не соответствовать этим условиям (бит 3 должен показывать, что принтер выключен, бит 4, что отменен выбор принтера, и бит 5, что нет бумаm). Приведенные ниже значения возвращаются . в регистр статуса соответственно стандарту "Эпсон", которому обычно следует IВМ: Значение Цепочка битов Инте11п11етация 223 11011111 принтер готов 87 01010111 принтер не, готов 119 01110111 нет бумаги в принт~;ре 247 11110111 принтер выключен Регистр статуса ввода имеет адрес порта на 1 больше, чем базовый адрес принтера. Базовый адрес для LPTI хранится по адресу 0040:0008, для LPT2 - по адресу 0040:ОООА и т.д. Имейте в виду, что если принтер был выключен, то ему требуется некото­ рое время на инициализацию после включения. Не начинайте печатать до тех пор, пока регистр статуса ввода не сообщит, что . принтер связан с машиной и rотов к приему данных. Высокий уровень Данная процедура проверяет, связан ли принтер с машиной, и объясняет пользователю, что делать, если нет. Она использует значения из приведенной выше "'Габлицы. Как уже отмечалось, такой подход не приемлем для процедуры общеrо назначения, которая будет обслуживать множество разных принтеров, но он вполне применим, когда вы пишете драйвер данноrо печатающеrо устройства. Отметим, что в строке 120 вычисляется двухбайтовое число, путем умножения старшеrо байт.а на 256 и добавления результата к младшему байту. Для нахождения адреса регистра
Принтер 389 статуса ввода к значению полученного базового адреса добавляется 1. 100 '"Получаем адрес LPTl и проверяем, готов ли принтер 110 DEF SEG = &Н40 ·указываем на область BIOS 120 РВ = РЕЕК(9) + 256*РЕЕК(8) + 1 130 IF INP(PB) 223 THEN 180 ·адрес регистра статуса ·если принтер готов 140 ВЕЕР 150 IF INP(PB) ·иначе - звонок и проверки 87 THEN locate 1,1: printMStrike the SELECT kеум GOTO 150 160 IF INP(PB) 247 THEN locate 1,1: print"Tum the printer опм: GOTO 160 170 IF INP(PB) <> 223 THEN 170 'ждем инициализации 180 '"Теперь принтер on-line -- можно начинать печать 190 LPRINT Z$ Средний уровень Для получения байта статуса из порта принтера используйте функцию 2 прерывания l 7H. При входе DX содержит номер LPT (0-2 для LPTl-LPTЗ). Эта функция сбрасывает три неисполь­ зуемых бита байта и делает операцию исключающего ИЛИ над двумя другими, поэтому значения битов отличаются от приве­ денных выше: Значение 144 24 184 Цепочка битов 10010000 00011000 101 IIOOO ... Интерпретация принтер готов принтер не готов принтер выключен И опять необходимо помнить, что эти значения меняются в зависимости от принтера. Наиболее общую информацию "выклю­ чен или не rотов" дает бит 3 статуса, равный О. Низкий уровень В следующем примере демонстрируется наиболее простая про- . цедура - проверка бита on-Iine регистра статуса. Для получения, байта статуса используется базовый адрес LPT l. ;---в сегменте данных MES DB. 'Printer not ready - strike any key when ОК$' ;---проверка, связан ли принтер с машиной (on-line). MOV АХ,40Н ;ES указывает на область данных BIOS
390 Глава 6 MOV ES,AX MOV DX,ES: [8] ;получаем базовый адрес INC DX IN AL,DX TEST AL,10008 ;смещение для регистра статуса ;получаем байт статуса в AL ;проверяем бит 3 JNZ GO _АНЕАD ;если принтер on-line, то вперед ;---печатаем сообщение об ошибке и ждем нажатия клавиши MOV АН,9 ;функция вывода строки LEA DX,MES ;DS:DX указывают на сообщение INT 21Н ;печатаем сообщение MOV АН, 7 ;функция ожидания ввода INT 2IH GO_AНEAD: ;ожидаем нажатия клавиши (без эха) ;продолжение программы 6.1 .3 . Интерпретация ошибок принтера и восстановление после ннх Проверка на ошибки не должна завершаться на том, что вы убедились, что принтер связан с машиной. Ошибки принтера. могут происходить в любой момент печати и программа должна быть готова восстановить ситуацию при сбоях. Хотя на принтере могут происходить самые разнообразные ошибки, только три типа ошибок возвращают информацию о себе в компьютер. Это - ошибка "отсутствия бумаг,~:", ошибка "отсутствия связи с маши­ ной" и общее сообщение "произошла ошибка". Как уже говори­ лось в [6.1 .2], не все принтеры сообщают об этих ошибках одина­ ково, но теоретически регистр статуса ввода использует следующие биты: бит3о 4О 5 когда произошла ошибка на принтере когда принтер не связан с машиной (off-line) когда кончилась бумага на принтере В частности, бит 4 может иметь другое назначение, нежели указано выше. Регистр статуса ввода имеет а,1рес порта, который на 1 больше, чем базовый адрес принтера. - Базовый адрес для LPTl хранится по адресу 0040:0008, для LPT2 - по адресу 0040:ОООА и т.д. ,, На низком уровне, когда программа посылает данные на прин­ тер, она постоянно обращается к биту 7 этого регистра, чтобы про­ верить, готов ли принтер принять очередной символ. Не сложно при этом проверить и бит 3, чтобы узнать о произошедшей ошибке. Если происходит ошибка, индицируемая битами 4 и 5, то
Принтер .... 391 по меньшей мере бит 3 будет равен О. Программа должна поста­ раться проанализировать ошибку, а затем попросить пользователя исправить ситуацию. Отметим, что функцию DOS, которая выво­ дит символы на принтер (функция номер 5 прерывания 21Н, см. [6.3.1 ]), можно заставить непрерывно проверять принтер на ошибку тайм-аута посредством команды MODE. Перед загрузкой программы, использующей функцию 5, надо ввести команду MODE LPTI: ,,,Р (еще. лучше поместить эту команду в файл AUTOEXEC.BAT, с тем чтобы она всегда выполнялась при заг­ рузке системы). Все эти ошибки приводят к тому, что печать остана1Jливается и должны быть предприняты какие-то действия, прежде чем она будет продолжена. Слишком огорчительно, если при возникно­ вении ошибки на принтере, пользователю программы придется печатать заново большую порцию документа. Тщательно проду­ манная процедура восстановления после ошибки позволит прог­ рамме возобновить печать с начала той страницы, на которой про­ изошла ошибка. Приступая к печати новой страницы, необходимо всегда запоминать местоположение указателя выводимых данных. Процедура восстановления может попросить пользователя вставить новый лист бумаги, а затем, как уже отмечалось, продолжить печать с начала той страницы, на которой произошла ошибка. Высокий уровень В Бейсике распознаются два кода ошибки для принтера. Код ошибки 24 возвращается, если был отменен выбор принтера, а код 27 - если принтер выключен или в нем _отсутствует бумага. Эти коды можно получить с помощью техники обнаружения ошибок, приведенной в [7.2 .5). К сожалению, эффективно отлавливается только код 27. Чтобы зарегистрировать код 24, требуется примерно полминуты, в течение которых программа заморожена. Не решит проблему и непосредственное чтение регистра статуса перед каж­ дой операцией печати. Это't метод сработает перед началом печати, но ничем не поможет, если во время печати произойдет отмена выбора принтера. Приведем процедуру обработки ошибок принтера: ♦ 100 ON ERROR GOTO 1000 ·устанаwшнаем обработку ошибок 1000 ···проверяем, произошла ли ошибка на принтере 1010 IF ERR = 24 OR IF ERR = 27 THEN GOSUB 2000: RESUME ·это ош~1бка принтера?
392 Глава 6 2000 ВЕЕР: LOCATE 1,1: PRINT"Printer not ready" 'сообщаем пользователю 2010 PRINT "Strike any key when ready" 'о проблеме 2020 IF INКEY$ = "" THEN 2020 'ожидаем ввода 2030 REТURN Средний уровень 'возвращаемся в программу Когда функция О прерывания 17Н выводит символ на принтер, она возвращает байт статуса принтера, АН. Проверяйте значение этоrо байта после посылки каждого символа. BIOS слегка модифи­ цирует байт статуса. Обычно бит О не имеет значения, но в данном случае он устанавливается, когда происходит ошибка тайм-аута (принтер не связан с машиной-). В следующем примере проверяются два типа ошибок: общая ошибка "принтер не готов'~ и ошибка "отсутствие бумаm". В примере предполагается, что в начале каждой· страницы (т.е. после каждого перевода формата) программа запоминает указатель начала выводимых данных, поме­ щая его в переменную STARTING_PTR . Это позволяет программе при возникновении ошибки повторить печать с начала страницы, а. не с начала всего документа. Конечно, принтер необходимо иници-:­ ализировать еще раз перед повторной печатью, так ·чтобы были восстановлены все его параметры. (Данный пример просто иллюст­ рирует проверку ошибок - он ни в коей· мере не является рабочей процедурой.) ;---в сегменте данных МESl DB 'Printer off-line - strike any key when ready$' МЕS2 DB 'Printer out of paper - strike any key when ready$' ;---посылаем символ и проверяем на ошибку NEXTC:MOV АН,О ;номер функции MOV DX,0 ;выбираем LPТI MOV AL, [ВХ] ;ВХ указывает на данные INC ВХ .' INT 17Н TEST АН,000010008 JZ NEXTC ТЕSТ АН,00100000В JZ OFFL MOV АН,9 ;увеличиваем указатель ;посылаем символ на принтер ;выделяем бит 3 (флаг ошибки) ;если нtт ошибки, то печатаем дальше ;выделяем бит 5 (отсутствие бумаги) ;переход, если с бумагой все в порядке ;готовим печать сообщения
Принтер LEA DX,MES2 INТ 21Н JMP SHORT REC OFFL: MOV АН,9 LEA DX,MESI INТ 21Н REC: MOV BX,STARТING_РТR MOV АН,О INТ 16Н CALLPRТR INIT JМР NEXTC ;DS:DX указывают на строку ;выводим строку ;уходим на восстановление ;готовим печать сообщения ;DS:DX указывают на строку ;выводим строку • ;восстанавливаем указатель ;функция ожидания ввода ;ждем ;инициализация принтера 393 ;начинаем печать с начала страницы 6.t .4 . Перекn1Очение между двум• иnи нескоnькими принтерами Компьютеры, оснащенные несколькими параллельными портами, могут иметь одновременно подсоединенными два или­ более принтера. Вывод может перенаправляться с одного принтера на другой двумя способами. Один способ состоит в том, чтобы использовать только такие операторы вывода на печать, которые указывают, на какой принтер надо осуществлять вывод. 1Вы можете написать такой код, который позволит вам изменять сдецификацию. Во втором способе переключения принтеров вывод на LPTl • происходит по умолчан_ию, но с указанием другого принтера, кото­ рый будет использоваться в качестве LPTl, что достигается изме­ нением базового адреса; относящегося к LPTI. Этот базовый адрес хранится в области даННЬiх BIOS ц ·ячейке 0040:0008. Поменяйте его с базовым адресом для LPT2 или 3 (хранящимися в ячейках 0040:ОООА и 0040:ОООС) и в, качестве LPTl будет испол~зоватъся другой адаптер. Высокий уровень В Бейсике, если принтер был открыт оnератором OPEN "LPTl" AS 1, то, чтобы переключиться на другой принтер, надо сначала написать оператор CLOSE # 1, а затем открыть другой принтер с помощью оператора OPEN "LPT2" AS #1. - Впослед­ ствии все операторы PRINT # 1 будут направлять свой вывод -на второй принтер. Это изменение труднее осуществить в прог­ раммах, использующих оператор LPRINT, поскольку LPRINT по умолчанию посылает весь вывод на LPTl. В этом случае вам необ-
394 Глава 6 ходимо поменять базовые адреса принтеров. Именно это и делает следующая программа на Бейсике, переключая LPT I и LPT2. Ее повторное использование переключает адреса обратно, возвращая систему к первоначальной конфигурации. 1()() DEF SEG · = &Н40 l10 Х =РЕЕК(8) 120 У = РЕЕК(9.) 130 РОКЕ 8,PEEK(I0) 140 РОКЕ 9,PEEK(ll) 150 РОКЕ 10,Х 160_РОКЕ 11,У 170 SYSTEM ·указываем на область данных BIOS ·получаем младший байт адреса LPTI ·получаем старший байт адреса LPTI ·переносим младший байт адреса LPT2 ·переносим старший байт адреса LPT2 ·посылаем младший байт LPTI II LPT2 ·посылаем старший байт LPТI в LPT2 ·выходим из Бейсика Эта программа будет очень кстати, если rотовое програм­ мное обеспечение не адресуется к нужному принтt-;ру. Ее можно откомпилировать и хранить на диске, скажем, под именем OTHERPRN, после чего надо будет только напечатать ее имя (в ответ на запрос DOS), чтобы переключиться с принтера на принтер. Если у вас нет транслятора с Бейсика, то создайте коман­ дный файл OTHERPRN.ВAT и поместите в него строку BASIC OTHERPRN. Когда вы напечатаете OTHERPRN, будет автома­ тически загружен Бейсик, который загрузит и выполнит прог­ рамму OTHERPRN.BAS, после чего вы вернетесь в операци­ онную систему. Необходимо, правда, чтобы на диске имелся интерпретатор Бейсика BASIC.COM. Помните, что вы должны устоять перед искушением испытать эту программу до тоrо, как она будет записана на диск, поскольку если вы ее запустите, она сотрет себя. • Низкий уровень Один из способов, с помощью которого программа на ассемб­ лере может переназначить принтер для вывода, состоит в исполь­ зовании для печати функции о· прерывания 17Н [6.3 .1 ]. Эта функция требует, чтобы номер принтера был помещен в DX. Заве­ дите переменную для этого номера, с тем чтобы он мог быть изменен в любой момент. Вторая возможность состоит в обмене базовых адресов LPTl и LPT2 или LPT3, что и демонстрирует сле­ дующая программа. Как и все короткие утилиты, 9на должна писаться в СОМ-форме, как объяснено в [ 1.3.6 ]. ;---обмен базо11ыми адресами LPТI и LPT2 MOV АХ,40Н ;сегмент области данных BIOS
Принтер моv ES,AX моv ВХ,8 моv DX,ES:(BXJ моv AX,ES: (ВХ] + 2 MOV ES:(BX],AX моv ES: (ВХ] + 2,DX ;ES указывает на данные ;смещение для базового адреса LPTI ;сохраняем базовый адрес LPТI ;сохраняем базовый адрес LPT2 ;меняем базовый адрес LPT2 • ;меняем базовый адрес LPTI 395 6.1. Установка спецификацин печати Для установки различных спецификаций, относящихся к формату страницы, стилю шрифта и т.п., на прин­ тер посылаются специальные управляющие коды. Эти коды посы­ лаются на принтер так же, как и любые другие данные. Некоторые из них это простые однобайтовые коды из числа первых 32 набора кодов АSСД. Подобные управляющие коды, перечисленные в (7.1 .9 ], инициируют такие простые действия принтера, как пере­ вод строки или перевод формата (прогон страницы). Однако боль­ шинство спецификаций печати устанавливается посылкой Еsс-пос­ ледовательностей, в которых один или более кодовых байтов сле­ дуют за символом Esc, имеющего код - ASCII 27. Начальный код Esc информирует принтер, что символ(ы), который следует за ним необходимо интерпретировать как команду, а не как данные. Такие Еsс-последовательности обычно не имею:r символа-огра­ ничителя, поскольку принтер "знает" длину каждой последова­ тельности. Только в некоторых случаях, когда последовательность может иметь разную длину, требуется ограничивающий символ, в качестве которого всегда используется код ASCII О. Почти во всех случаях спецификации, установленные этими кодами, действуют до тех пор, пока они не будут явно отменены. Как только будет получен код, например, подчеркивания, то оно будет осуществляться до тех пор, пока не будет послан код отмены подчеркивания. Буфер принтера может быть очищен без отмены установленных спецификаций. Но· если произошла ошибка на принтере и принтер был выключен и включен, то необходимо снова устанавливать все спецификации. Большинство устанавливающих спецификации принтера кодов перемешано с данными, на которые они действуют. Например, данные для слова, которое должно быть выделено жирным mрифrом, должны предваряться Еsс-последовательностью, включа­ ющей жирный· шрифт, и завершаться Еsс-последовательностью, выключающей ero. Поскольку универсальный стандарт на эти
396 Глава 6 коды отсутствует, то печать с использованием мощных возмож­ ностей требует, чтобы для каждоrо поддерживаемоrо принтера были написаны драйверы. Каждый драйвер преобразует инструк­ ции, генерируемые процедурой печати, в протокол, используемый данным принтером. В ассемблере посылка кодов осуществляется самым обычным образом, но, работая в Бейсике, вы должны помнить, что опера­ торы, посылающие управляющие коды (LPRlfliT или PRINT #), должны завершаться точкой с запятой. В проти~ном случае опера­ торы будут автоматически добавлять к посылаемым кодам пару возврат каретки/перевод строки. Приведенные ниже обсуждения и примеры в основном отно­ сятся к графическому принтеру IВМ. Коды, используемые этим принтером, настолько же "стандартны", насколько и любой друrой протокол. В большей степени это связано с тем, что этот протокол применяется в эпсоновских принтерах (первые принтеры для IВМ РС поставлялись фирмой Epson), которые составляют треть всех существующих принтеров. Управляющие коды, используемые принтерами IВМ, сравниваются в [6.2 .7 ]. Хотя информация, при­ веденная в данном разделе, может быть неприменима к тому принтеру, с которым вы работаете, но все-таки большинство общих принципов окажутся полезными вам. 6.1 .1 . Установка текстовоrо н rрафнческоrо режнмов Пока принтер специально не переведен в графический режим, он всегда находится в текстовом режим~. Команда, устанавлива­ ющая графический режим, должна сообщать, какое число байтов графических данных будет передано (но не больше одной строки), и после тоrо, как это число байтов будет интерпретировано как графическое изображение, принтер вернется в текстовый режим. По этой причине нет команды, которая переводит принтер в текс­ товый режим. Число графических режимов у разных принтеров различно. Во всех случаях за кодом, устанавливающим графический режим, следуют 2 байта, указывающие, какое число графических байтов будет передано (сначала младший байт). Чтобы вычислить значе­ ние этих двух байтов, разделите число байтов данных на 256 и поместите результат во втОJЮЙ байт, а остаток - в первый байт. За этими двумя байтами должны сразу следовать байты данных. Каждый байт определяет цепочку битов, соответствующих восьми вертикальным точкам одной позиции в строке. Младший бит (1) соответствует низу колонки, а старший бит (128) - верху. Например, чтобы напечатать пирамиду, пошлите сна чала байт, у
Принтер 397 котороrо установлен только нижний бит, затем байт, у которого установлены 2 нижних бита, и т.д. После восьмого байта распо­ ложите те же байты в обратном порядке. Значение первого байта будет 1, второrо - З (1 +2), затем - 7 (1 +2+4), 15 (l +2+4+8) и т.д. На рис. 6.1 изображена вся картина. 1 i"'с s :z: i(") 128 64 32 16 8 4 2 Байт 4567 9101112131415 о'оо ооооо ооооооо ооооооооо ооооооооооо оооооооооооо о ооооо 371531631272551276331 Рис. 6.1 . Цепочка битов для печапI шIрамиды Для печати пирамиды в Бейсике на графическом принтере IВМ напишите следующий код: 100 LPRINT CHR$(27);CHR$(75);CHR$05);CHR$<0);ПIR$(1 );CHR$(3); CHR$(7) ;CHR$(15) ;CHR$(31) ;CHR$(63);CHR$( 127) ;CHR$(255); CHR$( l 27);CHR$(63);CHR$(31) ;CHR$( 15) ;CIIR$(3) ;CHR$( 1); Первые два байта переводят принтер в графический режим с 480 точками, следующие два - сообщают, что будет передано 15 байт графических данных, а затем идет последовательность райтов .qанных. Конечно, то же самое можно запрограммировать у'мнее, 6рганизовав цикл, в. котором будут передаваться байты данных. Отметим, что в этом слу,чае проблемы возникнут, если указанное число байтов не соответствует числу посылаемых байтов. Чтобы создать пробел между графическими фигурами, выведите нес­ колько байтов с нулевым значением. В Бейсике, когда в одной строке выводится больше 80 байт •графических данных, не
398 Глава 6 забудьте предварительно установить "бесконечную" ширину прин­ тера. Для этого надо ввести команду WIDTH "LPTl:",255. Графический принтер IВМ имеет четыре графических режима, которые более или менее "стандартны". Они таковы: 27,75 480 точек в строке. Нормальный режим. Максf1мум 480 байт да~шых ,,а оператор. 27,76 960 точек в строке. Удвоенное горизонтальное разре111еш1е, но печап, вдвое медленнее (двойная плотность). Максf1мум 960 байт да11111,1х ,ш оператор. 27 ,89 960 точек в строке, печать с нормальной скоростью (двойная плотность с высокой скоростью). Две точки, прилегающие по горизонтали, не могут быть напечатаны, поскольку не будут успевать иголкf1 печата­ ющей rоловки. Если делается попытка flX 1шпечатать, то вторая точка будет игнорироваться. Максимум 960 байт да~шых на оператор. 27,90 1920 точек в строке, nе•~ать вдвое медленнее (•1ет11ср11ая плот1юсп,). Соседние точки по rоризонтали должны отстоwп, по крайней мере на З точки (т.е. 1 печатаем, 2 пропускаем). Максимум 1920 байт щ111111,1х ,ш оператор. В более плотных режимах две прилегающие по горизонтали точки не могут быть напечатаны. Чтобы заполнить пропуски между точками, верните каретку к левому полю, немного сдвию,те печатающую головку вправо и сделайте второй проход, испот,зуя те же данные. Сравним плотности печати, вызываемые 'одними и теми же управляющими кодами на разных принтерах: Коды Г(!а!Ыический L(вепюй Ком11акт11ый П1101111и1пс11 П(!ИНТе(! П(!flHTe(! ~ 27,75 480 точек 1108 560 41Ю 27,76 960 точек 2216 960 27,89 960 точек 2216 960 27,90 1920 точек 4432 1920 Из всех принтеров фирмы IВМ цветной принтер является уни­ кальным, так как он может устанавливать масштабный коэффи­ циент (aspect ratio) для графических изображений. Этот ко::,ффи­ циент отражает разницу горизонтальных и вертикат,ных рассто­ яний между точками. Обычно желателен коэффициент l: l, пос­ кольку в противном случае трудно проводить графические вычис-
Принтер 399 ления. Но при копировании графическоrо изображения с· экрана надо, чтобы масштабный коэффициент был таким же, как у дисп­ лея. В экранном режиме умеренноrо разрешения 5 точек по верти­ кали занимают тот же размер, что и 6 точек по rоризонтали. Это соответствует масштабному коэффициеН<rу 5:6, каковое и исполь­ зуется по умолчанию цветным принтером. Допускаются только коэффициенты 1:1 и 5:6. 6.2 .2 . Управnенне расстояннем между строкамн В принтерах, за исключением тех, в которых заложены воз­ можности графопостроителя, вся печать осуществляется построчно. Даже графические изображения рисуются построчно, хотя в этом случае нет пустых мест между строками. Код ASCII l О - стан­ дартный управляющий код перевода строки. Посылка ero на прин­ тер (без предшествующеrо кода Esc) приводит к тому, что бумага будет продвинута вперед на ука,занный интервал. Обычно, если код перевода строки не посылается за кодом возврата каретки, печатающая rоловка возвращается к левому краю бумаги и можно снова печатать на той же строке. Однако можно сделать и так, чтобы перевод. строки происходил автоматически при каждом воз­ врате каретки. Этим управляют переключатели на принтере. Toro же результата можно добиться, установив бит I регистра упра_в­ ления выводом (см. [6.1.0 ]) . Многие принтеры выполняют вклю­ чение и выключение автоматическоrо перевода строки с помощью управляющих кодов 27 ,53, а некоторые - осуществляют обратный перевод строки с помощью кодов 27, 93. По умолчанию графический принтер использует интервал печати, равный 1/6 .дюйма (т.е. выводят 6 строк на дюйм); к этому режиму всегда можно вернуться, послав управляющие коды 27 ,50 (эти коды используются также в сочетании с кодами изме­ нения интервала между строками, обсуждаемыми ниже). Для гра­ фическоrо принтера имеются еще два предопределенных меж­ строчных интервала: l /8 дюйма и 7 /72 дюйма. Соответствующие им управляющие коды 27,48 и 27,49. Возможна и более тонкая градация межстрочных интервалов. В графическом принтере применяются три кода, позволяющие изме­ нить интервал на очень малую величину. Все три управляющих кода используют двухбайтовую Еsс-последовательность, за которой следует число l /72 или l /216 дюйма, определяющее межстроч­ ный интервал. Вертикальное расстояние между центрами двух точек равно 1/72 дюйма. Интервал 8/72 дюйма не оставляет про­ межутка между строками (9 строк на дюйм). Стандартный интер­ вал 6 строк на дюйм задается числом, равным 12/72 дюйма.
400 Глава 6 Наконец, 1/ 216 соответствует 1/ 3 от 1/ 72. Изменение на такую величину позволяет печатающей головке слегка сдвинуться от центра строки, с тем чтобы точки при втором проходе заполнили промежутки, обеспечивая печать более высокого качества. Ниже приводятся Еsс-последовательности для управляющих кодов: Изменение 1/72 дюйма 1/216 дюйма 1/216 дюйма Еsс-последовательность 27,65,п (где п от I до 85) 27,51,п (где пот1до255) 27,74,п (где пот I до 255) Команды для изменения интервала, выраженного в 1/ 72 дюйма, не активизируются до тех пор, пока не встретится второй управляющий код: 27 ,50. Как объяснялось выше, этот код может также использоваться отдельно для восстановления стандартного интервала в 1/ 6 дюйма. Если ранее была применена команда 27,65,n, то для восстановления интервала в 1/6 дюйма надо пос­ лать команду 27,65,12,27,50. Два управляющих кода для интерва­ лов, выраженных в 1/216 дюйма, не идентичны. Первый код определяет, что все последующие переводы строки будут выпол­ няться с указанным интервалом; второй же действует только на один перевод строки, а затем возвращает интервал, установленный ранее. Следующая таблица сравнивает межстрочные интервалы, вызываемые одними и теми же управляющими кодами на различ­ ных принтерах фирмы IВМ: Коды Машич:- Графи- Цветной Компакт- Струйный "Ромаш- ПРQпринтер ный ч:еский .!!!!!f!: ковый" 27,48 1/8 1/8 1/8 1/9 1/8 1/8 1/8 27,49 7/72 7/72 6/72 1/9 9/96 7/72 27,50 1/6 1/6 1/6 1/6 1/6 1/6 1/6 27,51 п/216 п/144 n/216 27,65 n/72 n/72 n/72 n/72 n/72 27,74 n/216 П/144 n/216 Независимо от того, как изменяются межстрочные интервалы, принтер всеrда контролирует прямые и обратные движения листа, поэтому переход к началу следующей страницы делается всегда вовремя.
Принтер 401 6.1 .3. Уnравnенне двнженнем бумаrн Бумага в принтере передвигается с помощью команд перевода строки, вертикальной табуляции и перевода формата. Установкой переключателей на принтере определяется, будет ли принтер авто­ матически переходить на новую страницу при обнаружении пер­ <Iюрации между страницами. Если перфорация не будет пропус­ каться, то печать может завершиться прямо на верхнем краю оче­ редной страницы. Пропуск перфорации оставляет по три пустых строки сверху и снизу каждой страницы. На самом деле принтер не распознает перфорацию, вместо этого он, ·предполагая, что в начальный момент бумага выравнена на начало страницы, считает число переводов строки. Можно программно переопределить уста­ новку переключателей, посылая на принтер управляющие коды 27 ,56, чтобы он не делал пропуска перфорации, и 27 ,57, чтобы делал. пропуск перфорации. Графический принтер использует код, который определяет число строк, пропускаемых между страницами. Этот код 27,78,n, где n - число строк от l до 127. Например, применение кода 27,78;10 приведет к ТQму, что принтер будет пропускать по 10 строк. Если межстрочный интервал равен l /6 дюйма, то 11- дюймовая страница будет содержать 66 строк и после печати каж­ дых 56 строк принтер будет пропускать l О строк. Ваша же прог­ рамма должна позаботиться, чтобы в самом начале продвинуть бумагу на 5 строк, с тем чтобы 55 строк текста помещались в центре страницы. Если используется бумага, размер которой отличается от стан­ дартного 11-дюймовоrо, то можно измени.ть длину страницы, с тем чтобы пропуски перфорации происходили в нужном месте и чтобы перевод <lюрмата устанавливал бумагу в правильную позицию. Размер страницы может устанавливаться либо числом строк на странице, либо размером в дюймах. Чтобы установить число строк на странице, пошлите код 27 ,67 ,n, где n - число строк. Та же ,пос­ ледовательность используется и для установки длины страницы в дюймах, за исключением того, что длина страницы записывается в форме 0,n, где n может быть от l до 22 дюймов. Для стандартной страницы надо послать команду 27,67,0,11. 6.1.4. Уnравnенне Qоnоженнем печатающей rоnовкн Печатаемый текст распределяется по странице частично за счет движения бумаги [6.2 .3 ], а частично за счет движения печатающей головки. Головка может быть позиционирована в любое место, но не путем задания ее координат. Вместо этого
402 Глава 6 указывается ее смещение относите,,ьно самой левой позиции, которую она может занимать. У принтера нет датчиков, сообщающих текущее положение головки. Ваша программа при необходимости должна отслеживать положение головки. При :JТом считается разумным начинать печать с подачи управляющего кода 27 ,60, который сдвигает головку в самую левую позицию, не делая перевода строки (то же самое делает и код возврата каретки). При печати текста имеется несколько способов передвинуть головку в нужное положение. Она может сдвигаться вправо пода­ чей одного или нескольких символов пробела или табуляции и влево подачей одного или нескольких символов "возврат на шаг" или символа возврата каретки. Движения осуществляются непре­ рывно - не стоит воспринимать их как неч .-о похожее на действия обычной пишущей машинки. До тех пор пока ваша программа знает начальное положение печатающей головки, она может ком­ бинацией переводов строки, пробелов, табуляций и возвратов на шаг форматировать вывод в соответствии с вашими пожеланиями. Принтеры, которые умеют выполнять обратный перевод строки, могут использоваться и как графопостроители. В графических режимах возможно перемещение головки на малые доли дюйма. При печати текста вы можете войти в графи­ ческий режим, чтобы добиться разных промежутков между сло­ вами. К сожалению, этот процесс существенно замедляет печать. Смотрите пример в [6.3 .2 ]. Имеется специальный код, который заставляет головку всегда возвращаться в крайнюю левую позицию перед печатью очередной строки, отменяя двунаправленную печать. Хотя это значительно замедляет печать, однако при этом достигается более точное пози­ ционирование головки. Это особенно полезно при работе в графи­ ческом режиме. Для того чтобы включить однонаправленную печать, надо послать код 27 ,85, l, а чтобы вернуться к двунаправ­ ленной печати - код 27 ,85,0. 6.1 .5 . Установка поэицин табуляции В зависимости от принтера могут устанавливаться позиции горизонтальной и вертикальной табуляции (графический принтер IВМ не имеет вертикальной табуляции). Под горизонтальной табу­ ляцией понимаются смещения относительно левого края, выражен­ ные в пробелах. В некоторых случаях допускаются до 112 позиций горизонтальной табуляции. Аналогично вертикальные табуляции определяются как смещения относительно верха страницы, а измеряются они в межстрочных интервалах. Для большинства ,
Принтер 403 принтеров IВМ допускается не больше 64 позиций вертикальных табуляций. Первые два байта кода для установки горизонтальной табу­ ляции - 27,68, а-для установки вертикальной табуляции - 27,66. Для обоих типов табуляций далее идет строка байтqв, показы­ вающая позиции табуляции в возрастающем порядке. Эта строка должна завершатьс~ байтом ASCII О, который служит ограни­ чителем. Для установки горизонтальной табуляции в позициях 15, 30 и .60 пошлите на принтер код 27, 68, 15, 30, 60, О. Для уста­ новки вертикальной табуляции в строках 8 и 12 пошлите код 27, 66, 8, 12, О. Напомним, что если размер страницы отличается от стандартного, то он должен быть установлен перед определением позиций вертикальной табуляции. Вертикальная табуляция отме­ няется кодом 27,67. Отметим, что большинство принтеров не имеет установки полей как таковой. Левое поле может создаваться за счет вывода табуляции или ряда пробелов в начале каждой строки. Для точной установки полей перейдите в графический режим и выведите ряд байтов ASCII О. Правое поле создается путем ограничения длины строки. 6.2.6. Нзмененне wрнфта печатн Ширина страницы 8 1/ 2 дюйма позволяет напечатать в строке до 80 обычных символов, если все они имеют одинаковую ширину. Пропорциональная печать (см. (6.3 .3 ]) позволяет поместить в строке еще несколько символов. С другой стороны, плотная печать позволяет вывести в строке 132 символа, печать с двойной шири­ ной - 40 символов, а плотная печать с двойной шириной - 64 сим­ вола. Имейте в виду, что использование печати с разной шириной в одной строке приведет к трудностям при форматировании. Большинство матричных принтеров имеет набор режимов печа­ ти специальными шрифтами. Приведем перечень стандартных воз­ _можностей, предоставляемых графическим принтером IВМ. Плотная печать. Для включения режима плотной печати надо послать однобайтовый управляющий код 15, для выключения режима - код 18. Стандартная страница шириной 8 1/2 дюйма позволяет напечатать в этом режиме l 32 символа в строке. Печать с двойной шириной. Для того чтобы принтер начал печатать с двойной _шириной, надо послать на него управляющий код 14. Режим печати с двойной шириной необычен тем, что прин­ тер автоматически выключает этот режим, когда встречает символ
404 Гл,iва 6 возврата каретки или перевода строки. Поскол~,ку такой вид печа­ ти обычно используется для однострочных заголовков, то это свойство удобно. Чтобы выключить этот режим в середине строки, пошлите код 20. Выделенная печать. При выделенной печати каждый символ печатается два раза в одной и той же позиции. Точки становятся темнее, что и создает эффект выделения. Скорость печати при этом уменьшается вдвое. Для включения этого режима I пошлите код 27,69, для выключения - 27,70. Печать за два прохода. В режиме печати за два прохода бумага сдвигается на 1/216 дюйма перед вторым проходом печатающей головки. При этом получаются более заполненные буквы, которые к тому же выглядят ярче. Скорост_!> печати уменьшастсs, вдвое. Этот режим включается управляющим кодом 27, 71, а выключается кодом 27,72. • Печать с подчеркиванием. Печать с подчеркиванием может выполнs_~ться двумя способами. Графический принтер имеет режим подчеркивания, в котором линейка печатается под каждым сим­ волом, включая пробелы. Для графического принтера фирмы IВМ такой режим включается кодом 27,45,1, а. выключается кодом 27,45,0.. Принтеры, не имеющие режима подчеркивания, могут сделать линейки при втором проходе по той же строке, печатая символы подчеркивания (ASCII 95> в тех местах, где это нужно, и пробелы (ASCII 32) - во всех остал,,ных позициях. Второй проход достигается тем, что после первого прохода подастся код возврата каретки без кода перевода строки. Второй проход нс мешает прин­ теру правильно подсчитывать строки при вычислении размера страницы. Печать с верхними и нижними индексами. На графических принтерах текст с верхними и нижними индексами сжимается вер­ тикально. Для печати верхнего индекса пошлите управляющ11й код 27,83,0, а для печати нижнего - 27,83,1. Можно прямо переходить от одних индексов к другим. Для в1,1ключения печати индексов с тем, чтобы принтер оказался на текущей строке, пошлите управ­ ляющий код 27 ,84. Некоторые режимы не могут использоватьсs1 в комбинации с другими. Если вы хотите включит,, 4 режима одновременно, то проконсультируйтесь со следующей таблицей. В каждом из шести столбцов приведена допустимая комбинациs1.
Принтер 40S Режим печати 2 3 4 5 6 Нормальный х х Сжатый х х Выделенный х х За два прохода х х х С индексами х х х С двойной шириной х х х х х·х С подчеркиванием х х х х х х ) 6.1 .7 . Сравнение возможностей принтеров IBM В следующей' таблице сравниваются управтющие коды для принтеров фирмы IВМ. Не вся информация относительно кодов точна (обращайтесь к документации IВМ), а в ряде случаев опу­ щены уникальные коды. Предлагаемая таблица показывает диапа­ зон возможностей принтеров, а также выделяет коды, которые можно считать стандартными. Отметим, что коды для первых четыр'ех принтеров приведены в выпуtке "Возможности и адап­ теры" (Options and Adapters) из серии технических руководств, а коды для. остальных принтеров приведены в сопровождающих их руководствах по эксплуатации. ~ Функция Перемещение бумаги: 10 перевод строки 11 вертикальная табуляция 12 перевод фор,..ата 13 возврат каре_тки 27,52 установка начала страницы 27,56 иrнорироват~, отсутствие бумаги Принтер Матрич-Графи-Цвет-Компак- Струй-Ромаш- Про- ный ческий ной тный ный ковый принтер х х х х х ,х х х х х х х х х х х х х х х х х х х х х х х х х х х 27 ,57 отмена игнорирования Х • отсутствия -бумаги х
406 Глава 6 Принтер ~ Функция Матрич- Графи-Цвет-Компак- Струй- Ромаш- Про- ный ческий ной тный ный ковый принтер 27,66 установка верти- х х х х х кальных табуляций 27,66 очистка вертикальных х табуляций 27,88 установка пропуска х х х х х перфорации 27,79 отмена пропуска х х х х х перфорации Перемещение печатающей головки: 8 возврат на шаг х х ·х х 9 горизонтальная х х х х х х х табуляция 27,60 сдвиг головки в х х х левый край 27,64 установка индекса х горизонт. движения 27,68 установка rоризон- х х х х х х х талыюй табуляции 27,68 очистка rоризон- х тальной табуляции 27,77 автоматическое х форматирование 27,80 вкл./выкл. пропорцио- х х нальной печати 27,82 восстановление табу- х х х х ляций по умолчанию 27,85 вкл./выкл. однонап- х х равленной печати 27,88 установка х х лепоrо/правоrо поля 27,100 программируемый х пробел 27,101 програмю1руемый х возврат на шаг
Принтер 407- п2инте~ Код Функция Матрич-Графи-Цвет-Компак- Струй- Ромаш- Про- 1 ный ческий ной тный ный ковый принтер Межстрочные и межСИМВОJJьные интервалы: 27,48 межстрочный х х х х х х интервал 1/8 дюйма 27,48 межстрочный х интервал 1/9 дюйма 27,48 межстрочный· х х интервал 7/72 дюйма 27,49 межстрочный х интервал 7/72 дюйма 27,49 межстрочный х интервал 9/96 дюйма 27,49 межстрочный х интервал 6/72 дюйма 27,49 межстрочный х интервал 1/9 дюйма 27,50 начать программи- х х х руемый перевод строки по 27 ,65 !. 27,50 межстрочный х х х х х х х интервал 1/6 дюйма 27,51 программируемый х х перевод строки(n/216) 27,51 программи~уемый х перевод строки(n/144) 27,53 вкл./выкл.· автомати- хх х х х ~еского перевода строки 27,65 программируемый х х х х х перевод строки (n/72) 27,67 установка длины х х х х х х х страницы 27,74 программируемый х х перевод строки (n/216) 27,74 программируемый х \ перевод строки (n/144)
408 Глава 6 Принтер !So8 Функция Матрич-Графи-Цвет-Комnак- Струй- Ромаш- Про- ный ческий ной тный ный ковый принтер 27,93 обратный перевод х х строки 27,104 перевод на х пол-строки вперед 27,105 перевод на х пол-строки назад Управление шрифтами: 11 режим 15 символов х на дюйм 14 включение режима , х х х х х х двойной ширины 15 включение плотной х х х х х х печати 18 выключение плотной х х х х х печати 18 режим 1О символов х х х на дюйм 20 выключение режима х х х х х х двойной ширины 27,45 вкл./вlilкл. подчер- х х х х х х кивания 27,58 режим 12 символов х х х на дюйм 27,69 включение жирной х х х х печати 27,70 выключение жирной х х х х печати 27,71 включение печати х х х х х в 2 прохода 27,72 выключение печати х х х х х в 2 прохода 27,83 включение печати х х х х х индексов
Приитер 409 Принтер !О! Функци11 Матрич-Графи-Цвет-Компак- Струй- Ромаш- Про- ный ческий НОЙ тный ный ковый принтер 27,84 выключение печати х х х х х индексов 27,87 вкл./выкл. печати с х х х х х двойной шириной 27,91 включение цветного х подчеркивани11 27,95 вкл./выкл. overscore х Установка специальных щрифтов и цветов: 21,54 выбор набора х х х х х символов 2 21,55 выбор набора х х х х х символов 1 27,61 загрузка шрифта х х 27.73 изменение качества х х х t печати 27,92 печатать управ- х х х л11ющие символы 27,94 печатать все символы х х х 27,97 сдвиг ленты в конце х страницы 27,98 выбор 4-й полосы ·Х ленты 27,99 выбор 3-й полосы х ленты 27,109 выбор 2-й полосы х ленты 27,121 выбор 1-й полосы х ленты Графические режимы: 27,75 установка режима х х 480 точек
410 Глава 6 Принтер Код Функц~iя Матрич-Графи-Цвет-Компак- Струй-Ромаш- Ilpo- ный ческий ной тный IIЫЙ ковый принтер 27,75 установка режима х 560 точек 27,75 установка режима х 1108 точек 27,76 установка режима х х 960 точек 27,76 установка режима х 2216 точек 27,89 установка режима х х 960 точек с нормальной скоростью 27,89 установка реж11ма х 2216 точек 27,90 установка режима х х 1920 точек 27,90 установка режима х 4432 точек 27,91 установка разре- х шения/цвета 27,110 установка масштаб- х ного коэффициента Другие возможности: 7 звонок х х х х х 17 выбор принтера х х х х х 19 отмена выбора х х х х принтера 24 очистка буфера х х х х х х х 27,81 •отмена выбора х х указанного принтера
Принтер 411 6.3. Посылка данных lia прннтер Посылка данных на принтер тривиальна в языках высокого уровня, а для программирующего на языке ассем­ блера имеется ряд функций операционной системы, которые также делают задачу достаточно простой. Программирование на низком уровне требует больше работы, но зато предоставляет больше воз­ можностей. Как правило, процедуры печати низкого уровня посы­ лают символ на принтер, а затем постоянно проверяют регистр статуса ввода порта, к которому присоединен принтер. Следующий символ посылается только тогда, когда принтер сигнализирует, что он готов (принтер может не печатать символ сразу, а запасать его в своем буфере до тех пор, пока не будет получена целая строка символов для печати). Кроме того, процедуры низкого уровня могут испол~,зонап, прерывание принтера или могут ими;гировать действие этого прерывания. С помощью специального программирования можно сделать так, что принтер будет осуществляп, прерывание процессора, когда он готов к приему следующего символа. Процедура обработки прерывания посылает следующий символ, после чего процессор может продолжат~, заниматься своими делами. Этот метод используется для фо11о(юt1 печати (которую называют также спулингом). Поскол1,ку физические перемещения деталей принтера намного медленнее\ чем быстродействие электроники компьютера, то вывод символов на принтер занимает лишь малую долю процессорного времени. Применение прерывания позволяет использовать это время эффективно. При посылке данных на принтер требуются сравнитст,но небольшие усилия, чтобы добиться очень с.ложного вывода. Все сложные картинки, которые может выводить. принтер, можно получить за счет комбинирования текстовых и графических дан­ ных, а также многочисленных кодов управления принтером, обсуждавшихся ранее в этой главе. Комбинируя в одной строке текстовый и графический режимы, можно добиться выравнивания правого поля и пропорциональной печати. Кроме того, любой_ ГР,а­ фический принтер может создавать специальные символы произ­ вольного вида, а за счет аккуратного манипулирования надпечатки и межстрочного интервала могут выводиться любые симво.лы псевдографики.
412 Глава 6 6.3.1 . Вь1вод текстовых нnи rрафнческих данных на прннтер Процессор может быть занят только посылкой данных на прин­ тер или печатать в фоновом режиме, используя прерывания прин­ тера. Возможна и третья альтернатива, когда программа посылает символы на принтер через определенные интервалы, что можно рассматривать как "псевдопрерывание". Этот метод не так тесно координируется с работой принтера, как настоящее прерывание, но это несущественно, так как работа принтера не критична ко времени. Независимо от тоrо, как выводятся данные, каждый раз на принтер посылается только 1 байт данн·ых. Языки высокого уровня предоставляют функции, которые, казалось бы, выводят сразу целые строки, однако на самом деле эти функции разбивают строки на отдельные символы. Обычно языки высокого уровня посылают на принтер пару возврат каретки/ перевод строки в конце каждой строки, тогда как программы на ассемблере должны сами добавлять эту пару кодов. Из-з-а этого приходится немного больше программировать, но взамен вы получаете более совер­ шенную программу, особенно в отношении проверки ошибок. Высокий уровень Для посылки данных на принтер Бейсик предоставляет опера­ торы LPRINT и PRINT #. LPRINT не требует никакой подго­ товки, но для вывода оператором PRINT # вы должны предвари­ тельно открыть принтер в точности так ,же, как и файл, с помо­ щью оператора OPEN "LPTl" AS 1· или OPEN "LРТЗ" AS #2. Оператор LPRINT всегда адресуется к LPT 1, в то время как PRINT# может адресоваться к любому принтеру. Пара возврат каретки/перевод строки автоматически добавля­ ется в конце любоrо оператора LPRINT или PRINT #, если только он не завершается точкой с запятой. Для избежания ненужных переводов строки не забывайте завершать посылку любых управляющих кодов ТО'JКОЙ с запятой. То же самое надо сделать, если вы хотите, чтобы строки текста печатались подряд, прилегая одна к друrой. Однако имейте в виду, что многие принтеры_ не начинают печатать до тех пор, пока они не получат данные для целой строки. Это определяется либо символом возврат каретки, либо тем, что число переданных символов достигло 80 (или дpy­ roro числа). Не забывайте послать завершающий код возврата каретки, чтобы вытолкнуть последнк,ю порцию символов из буфера принтера.
Принтер 413 Принтер автоматически переходит I на с.,1едующую строку по достижении конца предыдущей. По умолчанию размер строки принтера равен 80 символам, но у принтеров с широкой кареткой это значение может быть больше. Строки, выводимые в режимах плотной печати или печати двойной ширины, могут иметь различ­ ную длину. Для изменения номера столбца, по достижении кото­ рого головка принтера перейдет на следующую строку, необходимо установить ширину принтера командой WIDTH "LPTl ",п, где п - требуемый номер столбца. Когда печатается строка, длина которой больше или равна ширине принтера, печатающая головка пере­ ходит на следующую строку, что эквивалентно выполнению кодов возврат каретки/перевод строки. Это означает, что в cJiyчae, когда длина строки равна ширине принтера, будут сделаны два перевода строки, если ;эта строка завершается, как обычно, парой возврат каретки/перевод строки. При графической печати принтер обычно устанавливают на бесконечную ширину. Чтобы сделать это, надо подать команду установки ширины, равной 255, WIDTH "LPTI ",255. Если вы забудете включить эту команду, то при выводе длинных последо­ вательностей графических данных Бейсик будет вставлs1п, пару возврат каретки/перевод строки после каждых 80 байт данных. Эти добавочные символы будут включаться в общее число байтов данных для графической печати, поэтому конец передаваемых данных будет просто опущен. Один оператор LPRINT может включат~, нескол1,ко :>лемdнтов данных в различных видах. Информация может содержап,ся в самом операторе, как, например, в LPRINT "The rain in Spain", или на нее можно ссылаться по имени переменной; как в случае Х$ = "The rain in Spain": LPRINT Х$. Специал1,ные символы. могут включаться за счет использования функции CHR$. Управ­ ляющие коды обычно посылаются именно этим способом, напри­ мер, LPRINT CHR$(10) посылает_ на принтер управляющий код перевода строки. Чаще всего CHR$ применяют при посылке кодов ASCII, которые нельзя ввести с клавиатуры. Любые из перечис­ ленных типов данных могvт быть объединены в одном операторе. Если вы хотите, чтобы различные :элементы данных печатались подряд, то разделяйте их точкой с запя:гой; в случае разделения их запятыми следующий элемент будет выводиться со следующей позиции табуляции. Это говорит о том, что оператор LPRINT фор­ матирует печать так же, как это делает· оператор PRINT при выводе на экран. Приведем несколько примеров: 100 LPRINT S$;" and ";У$ 110 LPRINT Х, У, Z ·комб~11~а1tю1 трех строк вывод т~>сх чисел
414 Глава 6 120 LPR!NT "The total is "; Х 'ко'1би11а10,1~1 строки 5-1 чис.:~а 130 lprint "The ";chr$(27);chr$(45);chr$(1 );"п:al";ch1·$(27);chг$(45);ch1·$(());" thing." 140 I10;I•IеIж>1вашIс cpe;11Icl'O слова Оператор PRINT # может использоват~, те же типы данных, что и оператор LPRINT, и он также позволяет включат~, нескол~,ко элементов данных в один оператор и смешивать различные типы данных. Точки с запятой и запятые действvют в нем анало­ гичным образом. Предлагаемые примеры, экв~валентны приве­ денным выше: 100 OPEN "LРТ!:" AS #2 110 PRINT #2,S$;" and ";У$ 120 PRINT #2,Х, У, Z 130 PRINT #2,"The total is "; Х 140 PRINT #2,"The ";chr$(27);ch1·$(45);chr$(1);"real"; chr$(27);chr$(45);chr$<0) ;" thing. " Средний уровень Функция О прерывания 17Н посылает один символ на принтер. Поместите символ в AL, а номер принтера - в DX. При возврате АН будет содержать регистр статуса, который надо постоянно про­ верять для обнаружения ошибок. В [6.1.3] объясняется, как это делать. Для вывода потока данных установите указатель на содер­ жащий их буфер и напишите процедуру, подобную следующей:. ;---вывод данных на LPTl MOV CX,NUMC MOV DX,0 NEXTC: MOV АН,О MOV AL,[BX] INT 17Н ;СХ содерж~п число байтов ,т1л>I в~.шо,rщ ;выбираем l,PTI ;функция rюсы;Iки симнола I1а IIр>1Iпер ;ВХ указыв;~ет 11а буфер да1111ых ;посылаем символ TEST АН,8 ;проверяем бит ош~1бки JNZ PR ERROR ;уход на обработку ошибки INC ВХ LOOP NEXTC ;увелич5-Iваем указате.,•н) ;выводим следующий символ Функция 5 прерывания 21Н - стандартное прерывание MS-DOS для вывода на принтер. Поместите символ в DL и выполните пре­ рывание. Эта функция всегда подразумевает ВЫВОД на LPT I и у нее нет возвращаемых регистров.
Принтер 415 ;---вывод даш1ых на LPTI MOV АН,5 :номеr, функ11нн MOV DL,CHAR ;1·оннн~.ч пс•~а1 ас',11,,,1 снчво;1 JNT 211-1 ;11осы;шем его на 11рн11 гср Другой способ вывода данных на принтер - с помощью функ­ ции 40Н прерывания 21 Н, которая яв;тяется функцией стандарт­ ного вывода и применяет метод дсс!<"риптор:1 фай:юв для доступа к фа11лу ил,1 устройству (см. !5.3 О];. П1,щ печати .л-а функция ш:по:1ьзует спсциа,,ьнь:й пr1:,1оп1х.:..:.1енный ном::р файа~а для принтера (номер #4), v.отор1,1й н<цо поместит~, ,; ВХ. Ф:у нкцин доступен только LPT 1, поэтому для вывод:1 на другой принтер вам надо бvдет поменять базовые адреса (см. [б.1.4 !} . DS:DX должны vказыiать на выводимые данные:, а СХ - СО.'!ержать число посыла­ емых байтов. Например: :---нывод 120 ба,,т :щнн,.1;.. щ1 LI'Tl MOV МО'✓ MOV LЕЛ INT JC Лi!.40Н n~\.. , 4 СХ,!~О DX,PRTR DATA 2\Н J-'!ПR ERROR - ;номер ф\·нк11.ш1 ;цнс"10 11осылае:-.1;)1л бn!'по:. ;DS:DX укаJыва,т на ;щ11111,•с ~посыл::н~м ;t;:11нiыt.: • При возврате устаноn:1енннй ф;1, 1 r n,:•1сноса индицирует ошиб­ ' ,v, при эточ АХ буд:~т содержат~, :; , ,_:'-_н1 принл::р нс связан с ,,:,,ш;~r-:ой, и 6, если ук:,зан нсверн1,1ii нvМ( 1 ' фзi'.~а. Отмстим, что п;)v ,_ ·по.1ь3ова1ши вредспрелс.1сн11:.,гl1 но•~сра файла открывап, ~,стt~ ·, 'RC не ну~(НО. Низкш, :;1, Байт данных посьс1:,.с.тся на :1р11нл.:р, а и~.1снно в регистр выво­ димых данных, адрес порта которого сштадаст с базовым адресом принтерз. Помните, что базовые адреса дли LPTI-LPTЗ хранятся со смещениями 8, 10 11 12 в обл,кти данных BIOS (начинающейся с 0040:0000). После того как данные посланы в регистр, на корот­ коt врсмх включается бит строба регистра управлс;ния выводом, адрес порта которого на 2 больше, чем д,111 регистра данных. Номер бита строба равен О, и он должен быть установлен только на очень короткое время, чтобы инициироват1, передачу данных, находящихся в регистре данных. Процедура печати может немед­ ленно сбросить бит строба обратно в О.
416 Глава 6 После того как байт данных послан, программа должна ожи­ дать сообщения от принтера, что он готов к приему следующего. Это делается двумя способами. При готовности принтер даст импульс в бит подтверждения регистра статуса ввода, адрес порт3 которого на 1 больше базового адреса принтера. Номе·р бита под­ тверждения равен 6 и обычно установлен в l. Импульс подтверж­ дения сбрасывает этот бит в О на достаточно долгое время, чтобы программа на языке ассемблера успела заметить это, если она пос­ тоянно следит за регистром . . Другой способ узнать о готовности принтера к приему следу­ ющего байта данных состоит в непрерывной проверке бита 7 регистра статуса, который сбрасывается в О, когда принтер занят, и устанавливается в 1, когда он готов принять данные. Если вы . пишете процедуру печати низкого уровня, которая должна рабо­ тать в интерпретирующем Бейсике или другом очень медленном • V языке, то применяите этот метод. В следующем примере базовый адрес LPT 1 берется из области данных BIOS, а ::щтем выводятся данные из буфера, на который указывает регистр ВХ. Программа постоянно проверяет регистр статуса на занятость и одновременно бит 3 на наличие ошибки на принтере. ;---подготовка MOV АХ,40Н ;ES указывает на обласп, щ11111ых nIOS MOV ES,AX MOV DX,ES: [8] ;получаем базош,1й адрес l,l'Тl II ОХ MOV BX,DATA_ST ;ВХ указывает Iia буфер да11111,Iх ;---посылаем символ NEXTC: MOV AL, [ВХ] OUT DX,AL INC DX INC DX MOV AL,13 OUT DX,AL DEC AL OUT DX,AL ;помещаем СИМIIОЛ 11 ЛI., ;посылаем симIюл ;DX будет указы11ап, ,ш рсп1стр ;управле11ш1 ш,шодом ;цеrючка бнто11 :1шI f1м11у;11,са строба ;посылаем с1-11·1шJ1 строба ;нормалыюс состоя11не регистра ;выключаем Сf1пшл строба ;---проверка на ошибку и ожидание rотовностf1 пршпера DEC DX ;DX указывает ,ш регистр статуса NOT_YET: IN AL,DX ;получаем байт статуса TESTAL,8 ;ошибка? JNZ PR ERROR ;переход 1ш обработку 01ш1бкн TEST AL,S0H ;при11тер зашп'!
Принтер JZ NOT УЕТ INC ВХ DEC DX JMP NEXTC 417 ;если занят, то возврат ;увеличиваем указатель в буфере данных ;DX указывает на регистр данных ;идем на печать следующего символа Если бJ!т 4 управляющего регистра принтера установлен, зна­ чит разрешено прерывание принтера. При работе по прерыванию программа не должна ждать сигнала готовности от принтера, непрерывно опрашивая регистр статуса принтера. Вместо этого она может послать символ и заниматься другими делами; когда прин­ тер будет готов для приема, следующего символа, то он пошлет сигнал подтверждения (бит 6 регистра статуса_ на короткое время будет установлен в 1) и автоматически будет вызвано прерывание принтера. Процедура обрабОJКИ прерывания пошлет на принтер следующий символ и вернет •управление программе, чтобы она могла продолжать свою работу, пока не произойдет следующего прерывания:. Когда все данные будут выведены, прерывание должно отключить себя. Прерывание принтера во мноrом аналогично коммуникационному прерыванию, которое обсуждается !J [7 .1.8 ]. К сожалению, оборудование сделано так, что не стоит пола­ гаться на это свойство для первого адаптера принтера. На одних адаптерах оно работает, а на других - нет. Только в случае после­ довательной/параллельной карты АТ вы можете довериться пре­ рыванию. Вместо него можно использовать прерывание таймера, как объяснено в [2.1.7 ]. Установите микросхему таймера 8253 так, чтобы принтер успевал вывести символ в промежутке между двумя прерываниями. Затем напишите процедуру обработки прерывания, которая посылает }Ja принтер очередной символ всякий раз, когда происходит прерывание времени суток. Для того чтобы обеспечить надежную синхронизацию, заставьте процедуру проверять бит занятости принтера ре111стра статуса (бит 7), и если принтер еще занят, процедура не должна посылать символ. 6.3 .1 . Выравннванне правоrо поля Реально выравнивание правого поля представляет собой расп­ реде.ление пробелов, находящихся в конце строки, равномерно по промежуткам между всеми словами. Некоторые принтеры имеют специальный режим, который автоматически осуществляет вырав­ нивание. Такую возможность имеет цветной принтер фирмы IBM; посылка управляющего кода 27,77 ,О заставляет электронику прин­ тера интерпретировать поступа~щие данные и форматировать их. В других случаях, когда выводится .символ пробела, принтер дол- 14 Р. джордейн
418 Глава 6 жен менять ширину пробелов между словами, переключаяс1, в гра­ фический режим. В графических режимах ширина пробела может изменяться на величину меньше или равную 1/ 6 от размера сим­ вола. К сожалению, многие принтеры на некоторое время останав­ ливаются при переключении между текстовым и графическим режимами, поэтому такой метод может оказаться слишком мед­ ленным. Другой способ состоит во вставке обычных символов про­ бела и_ распределении их как можно более равномерно по строке. Более сложный графический подход описан ниже. Шаги, которые необходимо предпринять для форматирования с выравниванием правого поля, могут быть следующими. Во-первых, из установленного формата стр,tницы должно быть вычислено количество символов в строке. Затем необходимо подсчитат1, число символов, занимаемое каждым из последовательно введенных слов, включая пробелы между словами. Отдельный счетчик _должен под­ считывать число пробелов. Когда общая сумма символов прев­ зойдет 80 (или ту ширину принтера, которая установлена), то пос­ леднее слово должно быть отброшено из этой суммы вместе с предшествующим ему пробелом. Чис.тiо оставшихся свободными позиций в строке _умножается на 6, поскоJН,ку к,tждый символ занимает размер шести точек по горизонтали, а получи·вшееся число делится на число пробелов между словами. После печати каждого слова принтер устанавливается в графи­ ческий режи,м 480 точек в строке и на неrо посылается ряд кодов ASCII О. Каждый такой байт сдвигает печатающую головку на одну точку вправо. Посылаемое число должно быть равно шести • для обычного пробела плюс резул1,тат распределения пустого пространства. Наконец, если остаток от деления нс нулевоii, то надо добавить по одному' добавочному байту к пробелам, начиная с первого, до тех пор пока остаток не будет исчерпан. Для примера рассмотрим случай, коrда строка состоит из 12 слов, содержащих 61 букву плюс 11 пробелов между словами. Это оставляет в строке с 80 символами 8 свободных позиций. Эти восемь позиций, умноженные на 6 точек, составляют 48 точек дополнительного пространства строки. ПnскоJН,ку в строке 11 про­ белов, то к каждому из них должно быт1, добавлено по 4 дополни­ тельные точки, после чего останутся еще 4 лишние точки, которые надо добавить по одной к первым Чf'!'Ырем пробелам. Тогда первые 4 пробела будут иметь размер 6 точек нормал1,ноrо пробела плюс добавочные 5, что в сумме равно 11. Остал1,ные пробелы этой строки будут размером в 10 точек. Чтобы вывести эти данные на принтер, подготовьте сначала код, посылающий на принтер I байт ASCII О, а затем поместите его в цикл, который должен быть .. выполнен столько раз, сколько таких байтов нужно вывести. На рис. 6.2 показан этот процесс.
Принтер 419 В приведенном ниже примере показаны основы выравнивания по правому полю. Не забудьте об обработке специальных случаев, когда слово длиннее строки (например, длинный ряд тире). Про­ цедура также будет нуждаться в изменении, если строка содержит всего несколько слов, как это бывает в конце параграфа. Не позволяйте ей распределить эти слова равномерно по всей ширине страницы. ПечатаемаА строка 1 Слово Слово· Слово Слово LJ LJ LJ LJ Слово Слово LJ c=::J б точек б точек б точек б точек б точек 28 точек ....__ __........___ ...__-,-_.. ____ .. ____..., 58 точек/5 пробелов = 11 точек/пробел + З дополнительные точки Есть лишние? Есть Есть лишние? лишние? Есть Есть лишние? лишние? Выровненнан Слово строка ..___r"-.......т----т"--"-г---т-L-....,г---,....~.---т''--....,_--~ 12 точек 12 точек 12 точек 12 точек 12 точек Рис. 6.2. Выравнивание по правому краю Высокий уровень В следующем примере SPTR указывает на место в буфере данных, с которого начинается следующая строка, выводимая на печать: 14* 100 S$ = "This text wi\l Ье printed with right justification using the printer alternately in text modes and iraphics modes." 110SPТR=1 'указатель в строке данных S$ 120COLS=1 130SPA=О 'счетчик позиции в строке 'счетчик пробелов в строке 140 '"вычисл11ем, сколько слов помещается в строке 150 С$ = MID$(S$,SPТR,l) 160 IF С$<> " "THEN 190 170 LASTSPA = COLS 180SPA=SPA +1 190COLS = COLS+1 'получаем символ 'если нет пробела, то двигаемся вперед ·иначе - запоминаем позицию 'увеличиваем чщ:ло пробелов 'увеличиваем указатель столбца
420 200SPТR=SPТR + l ·увеличиваем указатель данных ·если конец строки, то уходим Глава 6 210 IF COLS = 81 THEN 230 220 GOTO 150 ·иначе - переходим к следующему символу 230IFС$<>""THEN270 240 COLS = 79 250SPA=SPA-l 260 GOTO 340 •если последний символ не пробел, то уходим ·иначе - длина строки равна 79 •отнимаем последний пробел ·переходим к вычислению пробелов 270 С$ = MID$(S$,SPТR+ l,l) ·проверка на конец слова 280 IF С$<> " "THEN 300 ·если следующий символ не пробел, то уходим 290 GOTO 340 ·иначе - переходим к вычислению пробелов 300 COLS COLS - LASTSPA ·возвращаемся к концу слова 310 SPТR = SPТR - COLS + 1 ·возвращаем указатель 320 COLS = LASTSPA - 1 'убираем последний пробел 330SPA=SPA-l ·уменьшаем число пробелов 340 "'вычисляем число точек на пробел 350 EXSPAS = 80 - COLS ·вычисляем число пробелов в конце 360 TOTSPAS = EXSPAS + SPA 'добавляем к остальным пробелам 370 TOTDOT = 6*TOTSPAS ·вычисляем число точек 380 DOTPSP = TOTDOT/SPA ·получаем число точек на пробел 390 EXTDOT = TOTDOT MOD SPA 'остаток от деления 400 '"теперь печатаем первую строчку нашего текста 410 OPEN "LPTI:" AS #1 ·открываем принтер 420 PRPТR = 1 ·указатель на начало буфера данных 430 С$ = MID$(S$,PRPTR,I) 'берем символ 440 PRPТR = PRPТR .+ 1 450IFС$=""THEN500 ·увеличиваем указатель ·если пробел, то на обработку пробела 460 PRINT #1, С$ ·иначе - печатаем символ 470 IF PRPТR = COLS + 1 THEN 590 ·выход по концу строки 480 GOTO 430 ·иначе - печатаем следующий символ 490 '"вот процедура печати пробелов 500 PRINT #l, CHR$(27) + "К"; 510 NUMDOTS = DOTPSP ·переход в графический режим ·вычисляем число байтов АSСП О 520 IF EXTDOT = О THEN 550 ·если нет добавочных точек, уход ,530 NUMDOTS = DOTPSP + 1 ·иначе - добавляем точку 540 EXTDOT = EXTDOT - 1 ·уменьшаем число добавочных точек 550 PRINT #1, CНR$(NUMDOTS); ·посылаем число графических ·символов 560 PRINT #1, CHR$(0); 570 FOR N = 1 ТО NUMDOTS: PRINT #1, CHR$(0): NEXT
Принтер 580 GOTO 430 590 PRINТ #1, СНR$03) Низкий уровень 421 'пробел окончен, переходим к следующему 'символу 'в конце посылаем символ возврата каретки Соот11етствующая ассемблерная процедура слишком длинная и поэтому здесь не приводится. Она работает точно так же, как и процедура, написанная на Бейсике, за исключением того, что нет необходимости заводить отдельну,о переменную для хранения строки. Нужно просто установить указатели на начало и конец строки, которую надо напечатать. 6.3 .3 . Пропорцнонаnьная печать Вообще говоря, для пропорциональной печати требуется специ­ альный принтер, который хранит в ПЗУ информацию о ширине каждого символа. Цветной принтер IВМ имеет режим пропорцио­ нальной печати, который включается последовательностью 27, 78, 1, а выключается - 27,78,0. Программа, котора~ форматирует вывод на такой принтер, должна знать информацию о ширине каждого символа (ее можно найти в документации). Имея эту инфор­ мацию, она может вычислить, сколько слов поместится ,на одной строке. Имейте в виду, что некоторые матричцые принтеры автома­ тически выводят пропорциональный текст в режиме за два про­ хода. Если слова в строке разделяются добавочными пробелами в графическом режиме, то принтер может переходить ко второму проходу после печати каждого слова, вместо того чтобы повторить сразу всю строку. Поскольку принтеры относительно медленно меняют направление перемещения печатающей головки, то печать текста, выравненного по правому краю, в пропорциональном режиме может занимать очень много времени и оказаться "непо­ сильной ношей" для принтера. Эта проблема не возникает при однонаправленной пропорциональной печати. Отметим, что цвет­ ной принтер фирмы IВМ может комбинировать пропорциональную печать с автоматическим выравниванием правого края, что делает специальное программирование ненужным. Опытные программисты могут застав}lть любой графический· принтер печатать в пропорциональном режиме. Программа должна иметь в памяти побитовое описание каждого символа (см. (6.3.4 ]) . Вместо того чтобы посылать на принтер код ASCII, который вызы­ вает изображение символа из ПЗУ, используется данная цепочка битов для создания графического изображения строки текста.
422 Глава 6 • Затем вся нужная строка данных выводится на принт'ер в графи­ ческом режиме. Этот подход расходует много памяти на хранение • графических образов символов, однако он позволяет полностью контролировать выводимое изображение. Высокий уровень В данном примере включается режим пропорциональной печа­ ти, а затем выводится: первая строка выходных данных программы. Ширина пропорцr."нальноrо шрифта считывается в массив FONT- WIDTH из последовательного файла. 100 --·считываем массив ш~Iрин шрифта 110 DIM FONТWIDTH(l27) ·отводим массив для ш~Iрин 120 OPEN "FONTS" FOR INPUT AS #1 ·открыв.-~ем файл ширин, где 130FORN=32ТО127 ·хра11ятся ширины для кодов 32-127 140 INPUT # 1, FONТWIDTH (N) 150 NEXT ·читаем ширщIу из массива ·сJlедуlОЩИЙ ЭJleMeflТ 160 ·--вычисляем ,сколько символов поместится в строке, 170 CHARPТR = О ·указатель в буфере 180 UNE$ = "" ·храню· строку для вьшода 190 UNELENGTH = О ·счет•шк дл~шы в то•Iках 200 WHILE LINELENGTH <480 ·доб11w111ем до з111юJ111сшI11 строк~• 210 С$ = PEEK(BUFPTR + CHARPТR) 'берем с~IмвоJI из буфера да1111ых 220 LINELENGTH = LINELENGTH + FONTWIDTH(ЛSC(C$)) 230UNE$ = UNE$+С$ 240 CHARPТR = CHARPTR + 1 250 WEND 'доба~u111см к строке вывода ·у11еш1 1 IиI1аем указатель •псрсХОД~IМ К обработке ·следующе1'0 симuоJ1а 260 ·--по окончании строки возuращаемся к кош~у nocJ1eдfIero слова 270IFС$ =""THEN310 ·ес.iш послед1шй символ npoбe.r1, то уход 280 FOR N = LEN(UNE$) ТО 1 STEI' -1 ·идем вазад-от конца строки 290 IF MID$(LINE$,N,l) = ." " THE N 310 ·этот символ пробел? 300 NЕХТ'если нет, то берем СJ1едующиr1 310 LINELENGTH = N - 1 ·ссш1 да, то 11рсд1,щущ~1й - п<клсщшй 320 ·--инициализируем проIюрц~ю11ат,11ую IIсчап, ~• I1 <J Сы Jш ем да11111 ,Iе 330 LPRINT CHR$(27);CHR$(78J;CIIR$(I); ·у11рааu1яющие коды 340 FOR· N = 1 ТО LINELl::NGTH 350 LPRINT PEEK(ВUFPTR + N-I ); 360 NEXT ·д.1111 КаЖЩ)I'() C~IMIIOШI пе•шт:1ем его переход~Iм к следующему снмIк>JIу
Принтер 423 Низкий уровень Программа, написанная на языке ассемблера, -должна работать совершенно аналогично программе в приведенном выше примере. Одно из преимуществ ассемблера состоит в том, что для просмотра ширин символов можно использовать инструкцию XLAT. Помес­ тите символ в AL, DS:DX должны указь1вать на таблицу, после чего можно применять XLAT. Ширина символа будет возв·ращена в AL. ;---просмотр ширин символов LEA s~· .D AT A_BUFFER ;указываем на буфер даннhlх LEA BX,WIDTH_TABLE ;указываем 1ш табшщу шир~ш MOV AL, [SI] ;получаем байт да11111,1х XLAT WIDTH TABLE ;получаем его шн~fшу в ЛL 6.3 .4 . Печать спецнаnьных снмвоnов Большинство принтеров не поддерживает расширенный набор символов IBM, однако многие программы испот,зуют специал·ьные символы псевдографики. Очень выгодно иметь возможность печа­ тать эти символы, и это не так сложно сделать на любом матрич­ ном принтере, который имеет графические возможности. Вместо того чтобы обращаться к ПЗУ принтера, программа должна иметь всю информацию об этих символах и управлять принтером для вывода их на печать. ,. Сама по себе печать -специал1,ных символов тривиальна. Просто разбейте символ на шесть байтов, цепочка битов которых соответствует структуре точек в каждом из шести столбцов, сос­ тавляющих символ. Например, чтобы напечатать символ горизон­ тальной двойной черты, код ASCII которого 205, программа должна вывести цепочку битов 00l00100 шесть раз в режиме 480 точек в строке. Это количество соответствует ширине символа, поскольку 6 / 480 равно ,l /80 строки. Чтобы перевести принтер именно в этот графичеtкий режим, необходимо подать управ­ ляющий код 27,75. Затем пошлите число идущих вслед грi1фи­ ческих данных, которое передается в виде пары байтов, при,1см ·младший байт - первый. Наконец, следуют сами 6 бит данных, которые в данном случае равны сумме значений б~1тов 2 и 5 (4 + 32 " 36). Вся последовательность целиком выглядит так: 27, 75, ~~2~е3н~т~~ ~~~:• :О~н~~ f;ф~~;~:и:ы~~:0и::;аз9:о~~еияго:~~: дополнительные расходы времени машины ничтожны, по срав­ нению со скоростью операций принтера.
424 Глава 6 Имеется частная проблема, когда символы псевдоrрафики должны соприкасаться друг с другом по вертикали. Обычно прин­ теры печатают строку, состоящую из столбца с восемью точками, затем спускаются вниз на высоту 12 точек, оставляя тем самым поле размером в 4 точки между строками символов. Символы псевдографики должны печататься и в этом поле, а в некоторых . случаях они занимают в высоту 12 точек. Поскольку бол~,шинство печатающих головок имеет только 8 иголок, то единственным решением проблемы является печать таких символов за два про­ хода, продвигая бумагу перед вторым проходом. В этом случае символ перевода строки (ASCII 10) вообще не испол1,зуется. Вместо этого принтер попеременно делает интервалы высотой то в 8, то в 4 точки. При втором проходе часть иrолок будет на том месте, где уже имеются отпечатанные точки, поэтому надо, чтобы биты для этих иголок были сброшены в О (не работали). Чтобы продвинуть бумагу на высоту четырех точек, надо пос­ лать код 27, 65, 4, 27, 50, а на высоту восьми точек - 27, 65, 8, 27, 50. При этом вызывается автоматический возвр.1т каретки. Во время выполнения первого прохода rотовится временная строка текста, которая будет печататься при втором проходе. Еслн дан­ ный символ обычный, то в соответствующую позицию временной второй строки символов ·надо поместит,, пробел (ASCII 32). Но если встречается специальный rрафический символ, который дол­ жен печататься в четырехточечном поле, то надо поместит~, ero код ASCII в соответствующую позицию второй строки. Например: Позиция символа Код ASCII Код 2-й строки 205 205 23 32 98 32 32 45 111 114 32 32 67 105 110 32 32 89 103 32 32 32 10 205 205 В памяти должна храниться отдельная таблица цепочек битов этих символов для второго прохода. Для двойной вертикальной черты содержимое таблицы для первого прохода будет О, 255, О, 255, О, О, а для второго - О, 15, О, 15, О, О. Отметиr,t, что во вто­ ром и четвертом байтах для второго прохода верхние 4 бита сбро­ шены, чтобы не было надпечатки. Более кратко процесс описывается так, коrда начинается печ.ать, то в первую очередь проверяется, является ли данный сим­ вол пседографическим, и если нет - он посыл.~ется на печап, как обычный код ASCII. Во временную строку, используемую для печати второго прохода, вставляется пробел. Затем обр.~батывастся следующий символ. Когда встречается символ псевдоrраф11ки, то 6
Принтер 425 кодирующих ero байтов берутся из табщщы, принтер переводится в графический режим для вывода 6 байт и посылаются данные. Затем принтер автоматически возвращается в текстовый режим. В соответствующую позицию строки для второго прохода помещается код ASCII этого символа псевдографики. Процесс продолжается до конца строки, цосле чего делается1,проrон бумаги на высоту четы-· рех точек. При .втором проходе надо опять поочередно рассмотреть каждый символ. ·Если это пробел, то надо печатать символ пробела (т.е. не печатать ничеrо, а п~то продвинуть головку к .:леду­ ющему символу). Если же это графический символ, то надо найти соответствующие ему данные для второго прохода в отдельной таблице и напечатать ero таким же образом, как и при первом проходе. Используйте строку для второго прохода для каждой печатаемой строки. На рис. 6.3 показана эта процедура. Высокий уровень В следующем примере текст разделен на две колонки, при этом непрерывная линия разделяет страницу посредине. Для простот~ здесь печатается только одна строка, ,однако можно было напе­ чатать и целую страницу, если вставить цикл FOR/NEX.T в стро­ ках 325 и 505. Для демонстрации двух подходов при первом про­ ходе печатается по одному символу, в то время как при втором проходе печатается целая строка. 100 "'таблица данных для первого 11рохода (толr,ко кщ11>1 178 и 179) 110DATAО,О,255,О,О,О 120DATA4,4,255,О,О,О 130 ;,,аналогичная таблица для второго 11рохода 140DATAо,о.15,о,Р·О 150DATAО,О,15,О,О,О 160 "'помещаем пе~ую таблицу в массив 170 DIM FIRPAS$(45) ·описываем массив ;щ11111,Iх 11ерIюrо 11рохо11а 180FORN=1ТО2 \90У$=мм ·запl'l;шяем его 'У$ хранит 6 байт на с~IмIю;I 200FORМ=1ТО6:READХ:У$ =У$+CI-IR$(X):NEXT 210 FIRPAS$(N) = У$: NEXT помещаем 11 масс~ш 220 "'помещаем в масс~в вторую табщщу 230 DIM SECPAS$(45) ·описьшаем масс~ш щшных IпоJ)(>I'0 rrpoxoдa 240FORN=1ТО2 ·заполняем массиII д111I111.Iми 250У$=мм 260FORМ=1ТО6:READХ:У$ = У$+CHR$(X):NEXT
426 Чтение символа Переходим в графи­ ческий режим Глава 6 Берем данные из таблицы 1 и печатаем Да Вводим номер СИ'-'~r>"а во временную строку Временна11 строка 32323232185 --- ---- ------------ Делаем перевод строки в 4 точки Читаем временный символ Нет Делаем перевод строки и печатаем следующую Переходим в графи­ ческий режим Рис. 6.3 . Печать симишю11 псе11доrраф~1ки 270 SECPAS$(N) = У$: NEXT 280 "'печатаем текст следующей строки Берем данные из таблицы 2 и печатаем 290 ТЕХТ$ = "Неге is one column" + CHR$< 179) + "I -Icre is the second colt1mn" 300 ТЕМР$:. = SТRING$(80,32) ·создаем строку для 2-1XJ IIроход:1 310 GRAPH$ = CHR$<27) + CHR$(75) + CIIR$<6> + CI-IR$(0) 320 OPEN "LPТI:" AS #1 открываем 1IриIпер 330 FOR N = 1 то LEN (ТЕХТ$) 'для К.\Жi\()11) CИMll()Jla текста 3-40 С$ = MID$(TEXT$,N,I) 'берем сим1юл и 11ро11еряем его - 350 IF С$< CHR$O28) THEN PRINT #1,С$;: GOTO 400 360 '"предполагаем, что все остальные сим11олы - 11се111iо1·рафика 370 PRINT #l,GRAPH$; ·входим в l'рафический режим 380- PRINT #1,FIRPAS$(ASC 1 (~$) - 178) ·выводим 1-й проход
Принтер 390 MID$<ТEMP$,N) = С$ 400 NEXT • ·маркер в строке 2-iu 11рохода 410 "'смещаемся на 8 точек вниз и делаем 2-й проход 420 PRINT #l,CHR$(27) +CHR$(65) +CHR$(4) +CI-IR$O4I); 430Z$="" 'Z$ содержит строку для 2-ro прохода 440 F'OR N = 1 ТО LEN(ТEXT$)'w1я каждсго символ~ текста 450 С$ = МЮ$ (TEMP$,N, 1) 'берем ~им вол и обр.~батываем его 460IFС$ =CHR$(32)THENZ$ =Z$+"- .. : GOTO 480 470 Z$ = Z$ + GRAPЩ_ + SECPAS$(ASC(C$) - 178) 480 NEXT 490 PRINT #l,Z$ 500 PRINT #l,CHR$(10); Низкий уровень переходим к слt:дующему символу 'печатаем всю строку сразу 'добавляем в конце перевод строки 427 Программа на ассемблере использует тот же алrоритм, что и приведенная программа на Бейсике. Если имеется только нес­ колько символов, вы можете сэкономить место, сжав таблицу, с тем чтобы их положение в таблице не было пропорционально их позиции в наборе ASCII. Затем подготовьте небольшую таблицу индексов с помощью инструкции XLAT, которая поможет быстро искать данные в этой таблице. 6.3 .S. Копирование содержимоrо экрана на принтер (дамп экрана) Дамп текстового экрана сделать . .μ;остаточно просто, если все используемые символы содержатся в ПЗУ принтера и ни один из них не выводится со специальными атрибутами, такими, как под­ черкивание ИJIИ негативное изображение. В этом простейшем слу­ чае программе нужно лишь установить ширину принтера, равной 80 символам, а затем считывать символы поочередно из видео­ буфера, посылая их кhк непрерывный поток данных на принтер. _Если в ПЗУ принтера отсутствуют специальные символы, такие, как симв6лы псевдографики, то программа должна подготовить свою таблицу данных для этих символов и выводить их на принтер . в графическом режиме. Поскольку эти символы могут заходить в межстрочные интервалы, то может потребоваться специальное программирование (см. [6.3.4 ]). Каждый из специальных атрибутов символов создает свои проблемы. Проверяйте атрибут каждого символа при считывании его. из видеобуфера (в [4.1.З] обсуждается значение битов, соот-,
428 Глава 6 Байт 2з 5б78 110 о о о 210 о о о зlо о о ооооо 410 ооооо ~Байт s.jo о о ооооо slo ооооо о о 110 о о о вlо о гJ Рис. 6.4 . Дамп rрафн<~ескою экрш~а ячейки для одною снм,ю;~а ветствующее различным атрибутам). Когда символ выделен с помощью подчеркивания или повышенной интенсивности, то надо включать подчеркивание или печать жирным шрифтом на прин­ тере. Однако если символ выводится в негат·ивном изображении, то возникают те же проблемы, что и с некоторыми графическими символами: область негативного изображения должна простираться до верхнего края следующей строки. В этом случае надо, следуя указаниям из [6.3 .4,J, заполнить черным всю область при втором проходе. В зависимости от принтера вам может понадобиться соз­ дать специальную таблицу данных для вывода символов в нега­ тиве, поскольку, когда они будут печататься, окружающие точки могут находиться слишком близко одна к другой, затемняя изобра­ жаемый символ. В этом случае нс может быть и речи о печати в два прохода. Простым решением проблемы с негативным изобра­ жением является использование графического режима экрана для вывода текста, а затем перевод принтера в один из графических режимов и посылка на него копии графического экрана. Графические дампы создают свои проблемы. Байт данных принтера соответствует восьми вертикальным точкам, в то время
Принтер 429 как на экране байт представляе,т 8 горизонтальных точек. Поэтому требуется процедура преобразования, показанная на рис. 6.4. Надо сразу получать по 8 байт памяти экрана, выбирая такие, которые соответствуют области точек 8*8. Затем надо использовать логи­ ческие операции для перестановки битов, как показано в при­ мерах. Имейте в виду, что большинство матричных принтеров иска­ жают экранное изображение. Это происходит потому, что они работают с масштабным коэффициентом 1: 1, в то время как коэф­ фициент экрана 5:6 (масштабный коэффициент сравнивает число горизонтальных точек на дюйм с числом вер,:икальных точ,. ,< на дюйм). Точнее rоворя, искажение изображения на самом деле воз­ никает из-за масштабного коэффициента экрана, поскольку прог­ раммы должны специально менять данные для изображения, чтобы оно выrлядело так, как нам хочется (например, изображение окружности на экране создается выводом на него эллипса). Когда данные с экрана выводятся на принтер, то эти искажения должны обращаться. Некоторые графические принтеры имеют специальные режимы, В· которых можно выводить копию экрана· без искажения, а цветной прин:гер фирмы IВМ может менять масштабный коэф­ фициент в любом из своих графических режимов. Высоки,й уровень Приводимая процедура на Бейсике копирует текстовый экран, игнорируя с.пециальные атрибуты: 10 OPEN "LPТI:" AS #1 20 DEF SEG = &НВООО 30 PRINT #l,CHR$(13) 40FORG=ОТО3998STEP2 50 PRINT #l,CHR$(PEEK(G)); 60 NEXT •открываем принтер ·указываем на видеобуфер •сдвигаем головку влево ·для каждого байта буфера читаем его и выводим на принтер 'обрабатываем следующий байт Переброска цепочек битов для графического дампа в Бейсике требует слишком много времени. Поместите в массив (здесь БУТЕ$) восемь байтов, отвечающих области экрана 8*8 точек. Создайте второй массив (VERTICAL$) и обнулите ero элемен~, а 1 затем поочередно перебрасывайте биты элементов этих массивов следующим образом: 500FORМ=ОТО7 510FORN=ОТО7 520 Х = ASC(BYТES(N)) ·для каждого бита ·для каждого байта ·получ.'lем значение байта
430 Глава 6 530У=2*(7-М) ·маска для одного 11ключс111юI'0 бита 540Z=ХANDУ ·проверка этого бита u байте 550 IF Z <> О THEN VERТICAL$(M) = CHR$(ЛSC<VERТICЛL$(M) OR 2*N) 560 NEXT N 570 NEXT М Низкий уровень ·если 011 включен, то устанамиваем бит ·в соответствующей позиции 2-ro массива ·следующий бит 'следующий байт Язык ассемблера совершает битовые преобразования намного быстрее. Есть процедура, которая делает :пи преобразования очень быстро, поскольку она держит вес в микропроцессоре (она немного великовата, но вы можете использовать взамен алгоритм, пока­ занный в Бейсике). Во время работы процедуры 8 результирующих байтов хранятся в регистрах СХ, DX, ВР и DI. Байт экранных данных помещается в AL, а затем в АН передвигаются последовательно CL, СН, DL и DH. Каждый раз из AL в АН сдвигается один бит и, когда сделаны 4 сдвига, СХ и DX обмениваются с DX и ВР, после чего вес ото повторяется снова. Этот процесс повторяется для каждого из вос,,ми экранных байтов и, когда он завершен, преобразованное изображение хранится в регистрах микропроцессора, причем самый левый байт данных для печати - в CL. Содержимое регистров выводитсs~ на принтер и обнуляется, после чего процесс повторястси для следующих вос1,ми байтов экрана. Сначала получите 8 байт из видсобуфсра и поместите их в буфер с именем BUFFER. Поместите О в АХ, СХ, DX, ВР и DI. Затем необходимы следующие действия: LEA MOV GETBY: MOV BX,BUFER S1,0 AL, [ВХ] [SI] DOHAF: XCNG AH,CL SHL АХ,1 XCNGAH,CL XCNGAH,CH SHL АХ,! XCNGAI-1,CH XCNGAH,DL SI-IL АХ,1 XCNG AI-1 ,DL XCNGAH,DH ;указьшаем IШ буфер IIH}(eO)Щlllll,IX ;смещение 11 :~том буфере ;берем байт ;nолу•~аем CL,, CI-1 , DL и 1)11 ;сд1шI·ая бит из ЛJ,
Принтер SНL AX,l XCNGAH,DH ;---на•шнаем втору~q половину перемещения битов XCNG СХ,ВР ;обменив.'1ем содержимое СХ и DX 431 XCNGDX,DI СМР SI,7 ;если все байты nреобра~ов.'111ы, то печатаем JE ~RTBS INC SI ;иначе - переходим к следующему байту JMP SHORT GETBY ;---печатаем байты PRTBS: PUSH DX моv АН,5 MOV DL,27, INT 21Н моv DL,75- INT 21Н MOV DL,6 INT 21Н MOV DL,0 INT 21Н CALL PR2BS РОР СХ CALL PR2BS MOV СХ,ВР CALL PR2BS MOV DX,DI CALL PR2BS ;сохраняем DX ;функция вывода 11а принтер ;код Esc·· ;посылаем его ;код графическQГ(, режима ;посылаем его ;будет nос;iшю 6 байт ;посылаем _содерж~1мое СХ ;посылаем содержимое DX ;посылаем содержимое В!' , ;посылаем содержимое DI (переходим к следующей группе и:1 вос1,ми.б.'1йтоu) PR2BS: PROCNEAR MOV АН,5 ;функция печати моv DL,CL ;сначала CL INT 21Н ;печатаем моv DL,CH ;затем СН INT 21Н ;печатаем RET PR2BS ENDP
Глава 7. Ввод/вывод 7.1. Доступ к последовательном у порту При асинхронной связи машина посылает или принимает байты информации порциями по одному биту. Вре­ менные интервалы между байтами при этом несущественны, но очень.важны интервалы между отдельными битами байта. Сигнал на линии может быть высокого или низкого уровня, что соответ­ ствует логическим нулю и единице, и говорят, что линия отмечена (marking), когда уровень вЪ,сокий, и пустая (spacing), когда уро­ вень низкий. Линия поддерживается в отмеченном состоянии, когда по ней не:г передачи данных. При начале передачи байта данных сигнал падает в О, отмечая стартовый бит. Затем следуют восемь битов данных (иногда меньше) в виде набора высоких и низких уровней. Последний бит данных может сопровождаться битом четности, используемым для обнаружения ошибок, а затем в последователь­ ность включаются l или более стоп;-битов, которым соответствует высокий уровень. Эти стоп-биц,1 начинают отмеченное состояние, которое будет сохраняться до тех пор, пока нс начнется передача следующего байта данных; число стоп-битов существенно, пос­ кольку они устанавливают минимальное время, которое должно пройти перед следующим стартовым битом. На рис. 7.l показана эта последовательность. Конечно, передающая и приемная станции должны применять один и тот же протокол для цепочек битов и работать с одной и той же скоростью обмена (измеряемой в битах в секунду, называ-
• Ввод/ вывод 433 емых тщсже бодами). При обмене могут легко возникать.ошибки, поэтому коммуникацио.нное оборудование предоставляет разно­ образную информацию о статусе как самого порта, так и присо­ единенного к нему модема. Задачей модема является преобразо­ вание сиmала, генерируемого портом ко~муникации,.. в акусти­ ческий сиmал, который может затем· быть передан по телефон­ ному каналу. Большинство модемов предоставляет также дополни­ тельные коммуникационные возможности, такие, , как автомати­ ческий вызов и ответ, которые не поддерживаются самим портом коммуникации. i!! ~1 ~~ )s '° :r ij i·i ,С: •i~ ~~, Промежуток ar i 1D (J времени а1; ,-- - ·_s_ б_и_ т.,, .Аа_ н_н_ ы_х_ ~_,. .... ._ _ .~_,~• j 1 1 1 1 _J. __.J Рис. 7.l ..Передача одною байта последо11а~:ельш,1х да11111,1х 7.1 .1 . Проrра"'мнрованне мнкросхемь1 UдRT 8150 Последовательная связь настолько сложна, что были . разрабо­ таны специальные микросхемы, выполняющие работу по формиро­ ванию и синхронизации_ строк битов, составляющих последова-
434 Глава 7 тельные данные. Такие микросхемы называют у1tuверсаль1tым асинхртшым прием1tuко.ч-передатчико.м (universal asynchronous receiver transmitter или UART). IВМ РС использvет UART 8250 фирмы lntel. • Операционная система поддерживает 2 порта коммуникации, поэтому в машине имеются 2 микросхемы. Их базовые адреса хр".~­ нятся в ячейках 0040:0000 для СОМ I и 0040:0002 для СОМ2. (Базовый адрес - это двухбайтовый адрес порта, который является младшим из группы адресов портов, дающих доступ к UART.) На всех машинах, кроме PCjr, СОМ\ имеет базовый адрес ЗF8Н, а СОМ2 - 2F8H; PCjr имеет свой внутренний модем по адресу ЗF8Н, а COMI - по адресу 2F8H. Для удобства мы в дальнейшем будем всегда нумеровать регистры ЗFхН, но все сказанное в равной сте­ пени применимо и к регистрам 2FxH. Микросхема 8250 имеет 1О программируемых однобайтовых регистров, с помощью которых управляется и контролируется порт коммуникации. Большинство из них занимается инициализацией порта, процессом, в котором возникает много сложностей. Доступ к этим 1О регистрам осуществляется через сем 1, адресов портов. с номерами ЗF8Н - ЗFЕН (или 2F8H - 2FЕН). В пяти случаях. регистр, к которому получаем доступ через данный портJ зависит от того, как установлен бит 7 в регистре контроля линии, который является единственным регистром с адресом порта ЗFВН. Вот :ли регистры: ЗF8Н <OUT, бит 7 = О II Зf;ВН) ЗF8НON,бит7=ОIIЗl'ВШ ЗF8НIOUT,бит7 = 1вЗFВН) ЗF9НON,бит7=1вЗFВШ ЗF9Н(OUT,бит7=ОвЗFВН) ЗFАН (IN) ЗFВН (ОUТ) ЗFСН ЮUП ЗFDH (IN) ЗFЕН (IN) Рсп~стр хра~Iс11н>1 11срсдатчнка Рсп1стр да~IIII,Iх I1рисмшIка Дсл~пел1, скорости обмс11а (млад1ш1r1) Дсл~пел1, скорости обме11а (стар1~н1й) Рс1·нстр разре111ешI>1 11рсрI,111ашIя Рсп1стр идс11п1фика11ин 11рерI,IшIшI>1 Ре1·истр уIIра11лс11ия ш111ни Регистр уrIра11ле11ич модемом Рспн:тр статуса д1-1111-1н Рсп1стр статуса модема Из десяти регистров только шесть необходимы для простой пос­ ледовательной связи. Регистр хранения передатчика содержит байт данных, которые будут посланы [7.1 .6 ], а регистр данных прием­ ника - последний полученный байт данных [7 .1 . 7 ]. Регистры управления и статуса линии инициализируют и управляют ли"ией связи, используя скорость обмена, содержащуюся в двух регистрах делителя скорости обмена [7.1 .2 J. Из оставшихся четырех регис-
Ввод/вывод 435 тров регистры управления и статуса модема необходимы только для связи через модем [7 .1 .5 ]; а два регистра, связанных с преры­ ваниями - только в процедурах, управляемых прерываниями [7.1 .8]. Прерывания при связи используют в целях эффективности. Обычная коммуникационная процедура непрерывно проверяет регистр статуса линии, ожидая вводимого символа или указания, что все готово для передачи следующего байта данных. Поскольку процессор работает очень быстро, по сравнению с обычными ско­ ростями, с которыми передаются послед9вательные данные, то этот метод напрасно расходует процессорное время, которое может использоваться для обработки поступающих или передаваемых данных. По этой причине микросхема 8250 может быть установ­ лена в режим, вызывающий прерывание при появлении символа, возникновении ошибки и т.п. Это прерывание моментально вызо­ вет процедуру вашей программы, которая, скажем, будет пере­ дава'Рь следующиц символ из коммуникационного буфера. 7.1 .1. Инициаnнзацня посnедоватеnьноrо порта При инициализации ("открытии") порта коммуникации уста­ навливаются все его параметры. Эти параметры включают в себя • длину слова, число стоп-битов, установку четности и скорость обмена. Длина слова - это число битов, которое образует основную единицу данных. Хотя привычно работать с порциями по 8 бит, но для ·стандартных файлов ASCII (в которых все символы имеют коды, не превышающие ASCII 128) достаточно 7. бит, а для ,пере­ дачи численных данных - всего 4 бит. Высокий уровень Бейсик открывает коммуникационный канал как файл, и поэ­ тому ему должен быть присвоен идентификационный номер: OPEN "СОМ!: ......... , " AS #1 В кавычках должна быть помещена вся информация, необходимая для инициализации порта коммуникации, при этом каждый эле­ мент отделяется от предыдущего запятой. Инициализирующие данные всегда вводятся в следующем порядке:
436 Скорость обмена Четность Биты данных Стоп-биты Глава 7 - дается как целое число: 75, 100, 150, 300, 600, 1200, 1800, 2400, 4800 или 9600 бод. По умолчанию берется скорость обмена 300 бод. - вводится как односимвольный код: О для нечетной четности, Е - для четной и N - при отсутствии конт­ роля по че;ности. Могут быть также S, когда бит чет­ ности всегда равен О, и М, когда бит четности всегда равен 1. Если используются 8 бит данных, то надо ука­ зывать N; при передаче четырех бит не надо исполь­ зовать N. По умолчанию принимается Е. - дается как целое число 4, 5, 6, 7 или 8. По умол­ чанию берется 7. - дается как целое число 1 или 2, причем 2 значение по умолчанию для 75 и 11 О бод, а 1 - для остальных. Когда число битов данных равно 4 или 5, то 2 обознаqает l 1/2 стоп-бита. Такое зна'чение возможно при коммуникации, так как в этом случае бит является единицей времени и поэтому делим. Оператор OPEN "COMl:" AS #1 открывает COMl для связи со скоростью 300 бод с четной четностью, передавая 7 бит данных и 1 стоп-бит. OPEN "COMl:1200,O,8,l" устанавливает скорость 1200 бод, нечетную четность, 8 бит на символ и 1 стоп-бит. Отме­ тим, что вы можете завершить оператор OPEN выражением LEN = число, где число устанавливает максимальный размер блока, с которым могут работать операторы GET и· PUT (по умолчанию 128 байт). Имеется ряд команд управления модемом, которые также могут быть включены в эту спецификацию (в [7 .1.5 ] объяс­ няется специальная терминология, применяемая здесь). RS Подавляет сигнал "Запрос на посылку" (Request to send) .Если эта, команда опущена, то OPEN "СОМ" включает RTS. CS Вызывает проверку линии "Очистка посылки" (Clear to send). За этой командой может следовать значение (от О до 65535), опреде­ ляющее число миллисекунд, которые проистекут д.:> момента выдачи сигнала ошибки тайм-аута, например CS500. Значение по умолчанию прш1ято 1ООО, и О, если указан параметр RS. DS Вызывает проверку линии "Готовность набора данных" (Data set ready). Доnускается необяз.'lтельный параметр, как и для CS. Значение по умолчан~1ю - 1000. CD Вызывает проверку линии "Определение носителя" (Carrier detect). Допускается необязательный временной параметр, как и для CS. Значение по умолчан~1ю - О.
Ввод/ вы.вод 431--- ... -. LF Вызывает автоматическую пода•1у кода перевода строки (ASCII 10) после каждого символа возврата каретки (ASCII 13). Используется дnя последовательного вывода на принтер. РЕ Разрешает проверку четности, вызывая ошибку тайм-аута уст­ ройства при возникновении ошибки четности. Эrи специальные команды могут помещаться в любом месте оператора OPEN "СОМ" и в любом пор$1дке. Отметим, что для выполнения оператора ·OPEN сигналы CTS и DSR должны быть· установлены, в противном случае будет выдана ошибка тайм-аута устройства. В заключение приводим оператор OPEN "СОМ", содержащий все параметры, кроме RS1 и LF: OPEN "COМ:l:1200,O,7,1,CS2000,DS2000,CD,PE" AS #1 LEN = 256 / Средний уровень . Функция О пр~рывания 14Н BIOS инициализирует порт ком·- мунnкации. В DX должен находиться номер коммуникационного . канала (COMI = О, СОМ2 = l). В AL должен .содержаться байт инициализирующих данных, значение битов которого может быть следующим: биты 1-0 дnинаслова.10=7бит,11 =8бит 2 число стоп-битов. О = 1, 1 = 2 4-3 четность. 00 или 1О = нет, 01 = 11е•1еп1., 11 • чети. 7-5 скорость обмена. ООО 110 бод 001 150 fюд 010 - 300 бод 011 600 бод, 100 1200 оод 101 2400 бод 110 4800 бод 111 9600 бод В данном примере порт инициализируется со словом, равным 8 битам, одним стоп-битом и четной четностью. Скорость обмена - 1200 бод. ;---nрис1н'lиваем зна•1е11ия 11араметро11 11ерсмс1111ым MOV WORDLENGTH,00000011 В ;д.1шш1 сло_щ1 8 бт· MOV STOPBIТS,000000008 ; 1 стш1-б~1·r MOV РARIТY ,ООО 110008 ; 1 1спшя •1ст11ость MOV BAUDRAТЕ, 100000008 ;скоросл, 1200 бод
438 ;---инициализируем COMI MOV AL,0 OR AL,WORDLENGTH OR AL,STOPВITS OR AL,PARIТY OR AL,BAUDRAТЕ MOV АН,О MOV ох.о INT 14Н Низкий уровень Глава 7 ; 1 1нстнм ЛL ;уста1-1а11л1-111аем 11ужt11.1е биты ;функция инициаш1зации порта ;выбираем COMI ;инициализируем порт Независимо от того, занимаемся мы вводом или выводом, как минимум, 4 регистра микросхемы 8250 должны быть инициализи­ рованы для операций обмена. Это регистры делителя скорости об­ мена, регистр контроля линии и регистр разрешения прерывания. Инициализация скорости обмена. Делитель скорости обмена - это число, на которое надо разделить частоту системных часов (l 190000 Гц), чтобы получить желаемую скорость обмена. Напри­ мер, для скорости обмена 1200 бод делитель скорости обмена должен быть равен 96, поскольку 1190000/96 приближен,но равно 1200. Чем больше делитель, тем мен1,ше скоросп, обмена. Ско­ рость обмена 300 бод и меньше требует двухбайтового числа для делителя. Старший байт посылается в ЗF9Н (или 2F9H), а млад­ ший - в ЗF8Н (2F8H). В обоих случаях бит 7 регистра управления линии должен быть установлен в I перед засылкой значений, в противном случае по этим двум адресам значения будут адресо­ ваны в другие регистры (см. [7.1.01). Вот некоторые значения, требуемые для обычных скоростей обмена: СкоQОfть обмена 3F9H 3F8H 110 04Н 1711 300 0111 801-1 600 оон сон 1200 оон 6011 1800 оон 4011 2400 оон зон 3600 0011 2011 4800 001-1 1811 9600 001-1 0CII
Ввод/ «ывод I 439 . Всегда устанавливайте регистры скорости обмена первыми, так как они единственные, которые требуют установки бита 7 в регис­ тре контроля линии. После этого надо изменить содержимое регис­ тра контроля линии, сбрасывая бит 7, чтобы все остальные дос­ тупы' к регистрам были. правильными. Поскольку регистр контроля линии является регистром только для записи, то нет способа вер­ нуть бит 7 обратно в 1 без одновременной установки всех осталь­ ных битов этого регистра. Отметим, что PCjr использует другие делители, описание которых вы можете найти в техническом руко­ водстве. Инициализация регистра контроля линии. Значения битов регистра контроля линии, адрес порта котороrо равен ЗFВН (или 2FBH), могут быть следующими: биты 1-0длинасимвола.00=Sбит,01 =6бит,10=7б11т,11 =8бит 2 число стоп-битов. о = 1, 1 = 1,5, есл11 дл11на ПIМВОЛОВ равна пяти, иначе - 2 3 четность. 1 = генерируется бит четносп1, О = нет 4 тип четности. О = нечетная, 1 = •1етная S фиксация четности. Заставляет бит четности всегда быть О или !'. О = отменена 1=всегдаl,еслибит3=l&бит4=О илиl=всегдаО,еслибит3=1&бит4=1--. илиl =нетчетности,еслибит3=О 6 установка перерыва. Вызывает вывод строки нулей в иачестве сигнала отдаленной станции. О = ,запрещено, 1. = перерыв 7 меняет адреса портов других регистров Обычно биты 5-1 сброшены в О. Остальные описыв;~ют значе­ ния, определяемые протоколом обмена. Регистр разрешения прерывания. Даже если вы не используете •прерывания, все равно нужно произвести запись в регистр разре­ шения прерывания,, чтобы быть уверенным, что прерывания запре­ щены. Просто поместите в этот регистр О. Регистр идентификации прерывания можно игнорировать.. Инициализация остальных регистров связана с модемами. Ясно, что модемы нужны только для связи с удаленными устройст­ вами, а не для управления близлежаЩJ,fми, такими, как последова:­ тельный принтер. В [7 .1.5] объяснено, как 'инициализировать регистр контроля модема. В следующем примере из области данных BIOS берется базо­ вый адрес COMI, после чего различные регистры инициализиру-.
/ 440 Глава 7 ются для скорости обмена 1200 бод, семибитовых данных, четной четности, и одноrо стоп-бита. ;---получаем базовый адрес СОМ! MOV АХ,40Н ;ES указывает на область данных BIOS MOV ES,AX MOV DX,ES:[0J ;получаем ~зовый адрес СОМ! ;---инициализируем реrистры делители скорости обмена на 1200 бод ADD DX,3 ;указываем на реrистр контроля линии моv оuт DEC DEC моv оuт D-EC MOV OUT AL, 100000008 DX,AL DX DX AL,0 DX,AL DX АL,бОН DX,AL ;устанавливаем бит 7 ;посылаем байт ;указывае~ на старший байт делителя ;скорости обмена ;старший байт для 1-200 бод ;посылаем старший байт для 1200 бод ;указываем на младший1байт делителя 1 ,младший байт,делителя д,11я 1200 бод ;посылаем младший байт ;---инициализируем реrистр контроля линии MOV AL,0 ;обнуляем AL OR AL,10B ;длина данных 7 бит OR AL,0008 ; 1 стоп-бит OR AL,10008 OR AL, 10000В ADD DX,3 OUT DX,AL ;rенерируется бит· четности ;четная четность ;указываем на регистр контроля линии ;посылаем инициализационное зна•1ение ;---инициализируем реrистр разрешения прерьц1ания DEC DX ;указываем на регистр разрешения DEC DX MOV AL,0 ОUТ DX,AL ;прерывания ;зап~щаем прерывания ;посылаем байт 7.1.3. Установка текущеrо коммуникационноrо порта Имеются два способа, с помощью которых программа может определить; какой из коммуникационных портов должен использо­ ваться. Один из них состоит в указании номера канала в операторе программы. Второй способ состоит в написании программы для обмена через порт COMI, но изменении коммуникационною адап­ тера, доступ к кото~му идет через СОМ 1.
Ввод/вывод 441 Область данных BIOS содержит место для четырех двухбайто­ вых переменных, в которых· находятся базовые адреса коммуника­ ционных: каналов (MS-DOS поддерживает только первые два из них).-Вазовый адрес порта - это младший из группы адресов пор­ тов, через которые можно получить доступ к данному коммуника­ ционному каналу. Базовый адрес для COMl хранится в ячейке· 0040:0000, а дЛЯ СОМ2 - в ячейке 0040:0002. Для смены комму­ никационных портов надо прсх;то поменять эти два значения. Пов­ торная смена значений приведет к первоначальному назначению портов . . Выс окий уровень •В Бейсике оператор OPEN "СОМ" может имет·ь вид: OPEN' С$+ "1200,N,8" AS #2, где С$ может быть либо "COMI:", либо "СОМ2:". В ~ачестве альтернативы также использ)IЮТ РЕЕК и РОКЕ дЛЯ обмена базовых адресов: 100 DEF SEG :; &Н40 ·указы11аем на область данных ШОS 110 Х = РЕЕК(О): У = РЕЕК(!) ·з11110мшшем первые 2 байта • 120 РОКЕ О,РЕЕК(2): РОКЕ 1,РЕЕК(3) ·11ере11осим следующие два байта 130 РОКЕ 2,Х: РОКЕ 3,У Средний уровень ·засылаем запомне1111ые значе11ия Если программа обращается к комму·никационпому порту через прерывание 14Н BIOS, то СОМ-порт определяется содержимым DX, которое равно О или 1 (дЛя COMl или СОМ2>. Вместо того чтобы присваивать DX непоср~дственное значение, заполняйте его из п~ременной, которой может быть присвоено зна.чение О или l. Программы, использующие коммуникацщ>Нные функции 3 и 4 прерывания ·21н, всегда адресуются к ,COMI. В этом случае надо поменять базовые адреса: ' ;---обмен базовых адресов для СОМ! и СОМ2 MOV АХ,40Н ;ES указывает на область данных BIOS MOV ES,AX MOV DX,ES: [О] MOV AX,ES: (2) MOV ES: [О] ,АХ MOV ES: (2) ,DX ;помещаем 1-й базовый адрес в DX ;помещаем 2-й базовый адрес в АХ ;обмениваем адреса
442 Глава 7 7.1.4. Оnредеnенне статуса коммуннкацнонного порта Реmстр статуса линии микросхемы 1.]ART 8250 определяет протокол связи. Этот регистр имеет адрес порта на 5 больше, чем базовый адрес данного канала. Обычно он постоянно просматрива­ ется в процессе коммуникационного обмена. При передаче данных регистр сообщает, что предыдущий символ уже послан, позволяя программе записать новый символ поверх его. При приеме данных регистр информирует программу о поступЛ(;нии следующего сим­ вола, с тем чтобы программа могла прочитать его прежде, чем он будет уничтожен следующим прибывшим. Значения битов этого ~гистра следующие: бит о = байт данных получен = полученные данные были перезаписаны (предыдущий символ не был вовремя считан) 2 = ошибка четности (вероятно, из-за шума в линии) 3 = ошибка окружения (передача не синхронизована) 4 = обнаружен перерыв (получена длинная строка ед~1шщ, индици- рующая, что другая станция запрашивает конец передачи) 5 = регистр хранения передатчика пуст (в этот регистр должны помещаться передаваемые данные) б = регистр сдвига передатчика пуст (этот регистр получает данные 1 . 11з регистра хранения и преобразует их в последовательный вид) 7 = тайм-аут (устройство не свя~но с машиной) Высокий уровень В Бейсике сначала определите базовый адрес используемого коммунuкационного порта, затем добавьте к нему 5 и примените оператор INP для получения байта из этого порта. В приложении Б объясняется, как в Бейсике производятся битовые операции, которые необходимо проделать программе, чтобы интерпретиро­ вать значение этого байта. В следующем примере проверяется бит наличия перерыва: 100 DEF SEG = &Н40 ·указываем на область данных BIOS 110.ADDR = РЕЕК(4) + РЕЕК(5)*256 ·вычисляем-адрес СОМ2 120 Х = lNP(ADDR + 5) ·вычисляем адрес регистра статуса 130IFХAND16THEN500 ·переход на подпрограмму, если бит 4 500 '"начинаем процедуру обработки перерыва
Ввод/вывод 443 Средний уровень Функция 3 прерывания ·14Н BIOS возвращает в АН регистр статуса линии (AL получает регистр статуса модема [7 .1 .5 ]) . При входе DX должен содержат~;, номер коммуникационного порта, к которому осуществляется доступ, где СОМ 1 = О, а СОМ2 = 1. Как и в предыдущем, в этом примере проверяется наличие перерыва: MOV АН,3 MOV DX,l INT 14Н ;номер функLtии ;выбираем СОМ2 ;получаем байт статуса TEST АН,100008 ;о611аруже11 персрын? JNZ BREAK DETECT ;если да, то 11среходим к процедуре обработки Низкий уровень Следующий пример совершенно аналогичен приведенному на Бейсике. Из области данных BIOS считывается базовый адрес ком­ муникационного канала, к нему добавляется 5, а затем из полу­ ченного адреса порта считывается байт статуса. ' MOV АХ,40Н MOV ES,AX MOV DX,ES: (2) ADD DX,S IN AL,DX TEST AL, 100008 JNZ BREAK_DETECT ;ES указыщtст на область дшшых ВIOS ;получаем базо111,1й адрес СОМ2 ;добавляем 5 для регистра статуса ;получаем байт статуса ;бит 5 уста1юw1е11? ;если да, то переходим к обработке 11ереры11а · 1.1.s. Иннцнаnнзацня н управnенне модемом Имеется 6 линий, по которым модемы связываются с компью­ тером (усовершенствованные модели могут иметь добавочные линии по интерфейсу RS232). Вот их названия, сокращения и функции: От компьютера к модемv Data Terminal Ready (DTR) (готовность компьютера) Request То Send (RTS) (запрос на посылку) И11формирует модем, •по комш,ютер нключе11 и готов к связи Информирует м~щем, •1то компьютер ожщщет посылки дан11ых
444 Глава 7 От модема к комnьютерv -i>ata Set Ready (DSR) (rотовность модема) Информирует компьютер, что модем включен и rотов Clear То Send (СГS) (rотовность к посылке) Data Carrier Detect (DCD) (обнаружен носитель данных) Ring Indicator (RI) (индикатор звонка) Информирует компьютер, что модем rотов начать передачу данных Информирует компьютер, что модем связан с другим модемом Информирует комnьюtер, что телефонная линия, к которой присоединен модем, имеет звонок Сначала компьютер устанавливает сигнал DTR, а затем инст­ руктирует модем связаться с удаленной станцией. После того как модем установил связь, он устанавливает сигнал DSR. Этот сигнал информирует компьютер, что модем готов к связи и в этот момент компьютер может установить сигнал RTS. И если модем ответит сигналом CTS, начнется передача. Две стандартные линии, по которым компьютер управляет модемом, доступны через регистр контроля модема микросхемы UART 8250. Этот регистр имеет адрес порта на 4 больше, чем базовый адрес используемого коммуникационного канала. Значе~ ния его битов таковы: Регистр контроля модема биты 7-5 4 3 2 1 о (всегда 0) 1 = выход UART замкнут на вход добавочный пользователь назна•1ен на вывод #2 добавочный пользователь назна•1ен 11а вывод #1 "запрос на посылку" активен 1 = "rото'вность компьютера" активна Обычно биты О и 1 регистра контроля модема установлены, а остальные - равны О. Бит 2 равен О, за исключением случаев, когда производитель модема предназначил его для специальных нужд. Бит 3 установлен только в случае, когда используются пре­ рывания [7.1 .8 ]. Наконец, бит 4 предоставляет возможность тести­ рования коммуникационных программ без установления реальной связи. Выходной сигнал микросхемы UART подается на вход, как будто UART принимает последовательные данные. Это свойство можно применять для тестирования правильности работы самой микросхемы. Однако при использовании коммуникационных про­ цедур прерывания 14Н BIOS оно недоступно.
Ввод/вывод 445 Четыре линии, по которым модем посылает информацию ком­ пыотеру, управляются реmстром статуса модема. Этот регистр расположен по адресу порта на 6 больше, чем базовый адрес используемого коммуникационного адаптера. Вот значение его битов: • • Регистр статуса модема бит 7 = DCD 6 =RI 5 DSR 4 CTS з изменение в DCD 2 изменение в RI изменение в DSR о изменение в CTS Программа непрерывно проверяет эти биты в ходе коммуника­ ционных операци~. Отметим, что 4 младших бита параллельны старшим четырем битам; Эти биты устанавливаются в l только тогда, когда происходит изменение в статусе соответствующего старшего бита с момента, когда регистр читался последний раз. Все 4 младших бита автоматически сбрасываются при чтении регистра.. Программы любого уровня могут непосредственно читать этот регистр. Другой возможностью является использование функ­ ции З прерывания 14Н ВIOS, которая возвращает регистр статуса модема в AL (при этом в АН будет содержаться регистр статуса линии). При входе DX должен содержать номер коммуникацион­ ного канала (0 или l). Многие модемы имеют гораздо больше возможностей по срав­ нению с теми, что отражены в двух связанных с модемом реrис­ трахJ Имеются возможности автоматической связи и автоматичес­ кого· ответа, которые контролируются управляющей строкой. Эта строка посылается в модем в виде обычных данных. Модем выде­ ляет управляющую строку из данных по специальному символу, используемому только для указания начала управляющей строки. Этот символ может быть предопределенным (часто используется код Esc - ASCII 27) или выбираемым пользователем. Модем спосо- • бен определить, насколько длинной должна быть каждая строка, поэтому по окончании строки он оп_ять рас~матривает входящий поток информации как данные. Каждый модем имеет свой набор команд. В качестве примера рассмотрим команды, используемьiе внутренним модемом PCjr:
446 Глава 7 Символ Значение Применен.не А ответ вход в режим ответа Вп перерыв посылает сигнал перерыва п х l 00 мс Сп отсчет п отсчитывает п звонков до ответа Dn... n вызов посылает строку чисел п... п Fn формат устанавливает протокол связи н разрыв прекращает связь с машиной инициализация инициализирует модем lR долгий ответ меняет используемую кодовую систему м режим модем принимает символы как данные Nn новый меняет командный символ на п о originate вход в режим originate р pick-up вход в режим голоса Q запрос запрос статуса модема R повтор повторить команду связи Sn скорость выбор скорости обмена Тп...п прозрачность игнорировать управляющие строки в следующих n... п байтах у. голос перевести модем в режим голоса w ожидание ничего не делать до следующей команды х передать передача тона вызова z тест проводит диагностику оборудов.'lш1я В ответ на команду запроса модем выдает информацию о своем состоянии, посылая ее в UART как обычные данные. Помимо про­ чей информации может сообщаться, что линия занята. Чтобы пра­ вильно использовать команды управления модемом и информацию о ero статусе, надо тщательно изучить документацию на данный модем. Модем PCjr описан в техническом руководстве по PCjr. Приведенные ниже примеры дают только общую схему установ­ ления связи через модем. Высокий уровень Поскольку телефонная линия очень медленная, то связь с модемом это одна из областей, где программирование ·на Бейсике ничем не хуже, чем на языке ассемблера. Приведем упрощенную схему: 100 ОUТ BASEADDRESS + 4,1 ·устанавлИв.'!ем бит DTR 110 "'теперь посылаем управляющую строку для вызова l-l'установле- 120 "'ния связи - этот код меняется от модема к модему
Ввод/вывод 447 ' 200 Х = INP(BASEADDRESS + 2) 210IFХAND2<>2THEN200 220 OUT BASEADDRESS + 4,3 1;юлу•шем регистр ст!!туса модема 1 ·ждем пока будет уста1108:lен бит 1 ·устанавливаем бит RТS 230 Х = INP(BAS~ADDRESS + 2) 240IFХAND11<>1THEN230 250 ... теперь посылаем данные получаем регистр статуса модема ·ждем пока будет установлен бит О Низкий уровень / Схема связи через модем на языке ассемблера выглядит так: ;---устанавливаем сигнал DТR MQV DX,BASADR ;начинаем с базового адреса ADD DX,4 MOV AL,l OUT DX,AL ;указываем на ~гистр контроля модема ;устанамиваем бит 1 ;посылаем в порт ;---посылаем управляющую строку модему w1я вызова (этот код различен для разных ти1юв модемов) ;---ожидаем пока будет устаноw1ен сю·1шл DSR INC DX INC DX ТRYAG: IN AL,DX TESTAL,108 Jz· TRYAG ;---устанавливаем бит RTS DEC DX DEC DX MOV AL,3 OUT DX,AL ;---ожидаем сигнала CTS INC DX INC DX ON_MO:IN AL,DX TESTAL,l ;указываем на ре1·истр статуса модема ;получаем· содержимое ;проверяем второй бит ;ждем пока 011 не будет равен 1 ;возвращаемся к регистру уnраОJ1ения ;устанамнваем сиг11ал RTS ;посылаем в порт ;возвращаемся к ре1·истру статуса ;получаем байт статус~, ;проверяем бит CTS •JZ ON_MO ;не продолжаем, rioкa 011 не уста11овлен ;---теперь можно 'посылать данные
448 Глава 7 7.1.6. Передача данных Передача данных проще, чем их прием, поскольку программа полностью контролирует состав данных и скорость, с которой они должны посылаться. Тем не менее процедуры передачи могут быть 1 достаточно сложными, если они обрабатывают данные по мере тоrо, как они посылаются. Могут быть также проблемы с синхро­ низацией при использовании протокола XON / XOFF. Этот прото­ кол использует коды ASCII l7(XON> и l9<XOFF>, для того чтобы сигнализировать принимающей станции, что передатчик хочет продолжить передачу временно прерванного потока данных. Чтобы принять эти сигналы, программа должна непрерывно анализиро­ вать принимаемые символы при передаче (в полнодулдекснсiм режиме, в котором обычно работают модемы, сигналы одновре­ менно идут в обе стороны по телефонному каналу).. Кроме тоrо, чтобы обнаружить, что удаленная станция посылает строку нулей в качестве сигнала перерыва, должен непрерывно анализироваться статус бита перерыва (номер 4) рег..истра статуса линии [7.1 .4 ]. На рис. 7.2 (см.· [7.1 .7 ]) показано, как процедура передачи данных взаимодействует с кодом, принимающим данные. Вследствие этих причин представленные здесь процедуры, отдельно передающие данные, являются искусственными. Но их можно скомбинировать с процедурами приема данных, описан­ ными в [7.1 .7), для создания общего представления. Ясно, что для создания действующей процедуры необходимо затратить бол1,шис усилия, особенно в части обнаружения и исправления ошибок при передаче данных .. Высоки~ уровень Для тоrо чтобы в Бейсике послать данные в отКР.ЫТый комму­ никационный порт, надо использовать операторы PRINТ.#, PRINT# USING или WRIТE#. Последние два оператора имеют специальный формат, параллf:)lьный применяемому им·и при выводе на дисплей. Обычно используется оператор PRINT #. В следующем примере посылаемые данные берутся непосредственно с клавиатуры. Предполагается, что COMl уже открыт, как пока­ зано в [7.1 .2). Процедура обрабатывает бит перерыва в регистре статуса линии. 500 С$ '= INKEY$: IF С$ <> "" THEN PRINT # 1,С$ ·если кла~тша щ1жата, носыJшем ее 510 Х INP(BASEADDRESS + 5) •1итаем регистр статуса ли11ш1
Ввод/вывод 449 520IFХAND32 32THENIООО ·проuерием бит 11ерерьша 530 IF EOF(l) THEN 500 ·если буфер 11уст, то ждем шюд.1 (здесь расположена процедура 11риема данных) 1000 '":щесь процедура обработки перерыва Средний уровень Функция 1 прерывания 14Н BIOS посылает символ, находя­ щийся в AL, в коммуникационный канал. При входе DX содержит номер порта (О или 1). При возврате АН 1получает байт статуса, в котором бит 7 = 1, если операция неуспешна. В этом случае имеют значение следующие биты: бит 4 обнаружен перерыв (сиmал "стоп" от 11ринимающей станции) 5 регистр сдвига передат•1ика пуст б регистр хранения передатчика пуст MS-DOS имеет функцию для передачи по коммуникационному· каналу символа, помещаемого в DL. Это функция номер 4 преры­ вания 21Н, но у нее- нет никаких преимуществ перед функцией BIOS; она •не возвращает статусной информации и не .позволяет назначать, какой из коммуникационных портов надо ис-nЬльзовать (это всегда COMI). Чтобы. вывести строку данных, !JОСПользуйтесь функцие~i 40Н прерывания 21Н. Это обычная функция вывода для всех файлов и устройств методом доступа дескриптора файлов. COMl имеет предопределенный номер - #3. Поместите но~ер файла в ВХ, 1 а число передаваемых байтов в СХ. Пусть DS:DX указывают на буфер выводимых данных, затем вызывайте функцию. ·моv АН,40Н ;номер функции МОV ВХ,3 ;предопределенный ~омер файла для СОМ 1 моv СХ,50 ;выводим 50 байт LEA DX,BUFER ;DS:,DX указывают на l)уфер да1111ых INT 2IН ;посылаем данные JC .COM_ERROR ;уход на обработку ошибк~1 Отметим, что при использовании предопределенных номерОв файлов ,их не надо открыват1;,. Если произошла ошибка, устанавли- 15 Р. Джордейн
450 Глава 7 вается флаг переноса, а в АХ возвращается 5, если коммуникаци­ онный порт не rотов, и 6 при указании неверного номера файла. Низкий уровень Когда байт данных помещается в регистр хранения передат­ чика, то он автоматически выводится в последовательный канал через регистр сдвиrа передатчика, который сериализует данные. Нет необходимости в импульсе бита строба, как это делается в случае параллельного адаптера. Бит 5 регистра статуса линии показывает, свободен л11 регистр хранения передатчика для приема давиых. Регистр постоянно проверяется до тех пор, пока бит 5 не станет равинм 1. После этоrо в регистр хранения передатчика посылается очередной байт. В процессе передачи бит 5 равен О, и только когда он опять станет равным 1, в регистр хранения пере­ датчика может бЬIТЬ послан следующий символ. Этот процесс пов­ торяется до тех пор, пока в этом ер-ь необходимость. В следующем примере даны основные понятия об этой проце­ дуре. Конечно, она ·может быть создана необычайно сложной (в частности, программирование связи требует особо тщательных процедур обнаружения ошибок и восстановления при сбоях). В примере предполагается, что коммуникационный порт и модем уже инициализированы, как показано в [7 .1 .2 ] и [7 .1.5 ]. Первая часть это цикл проверки ошибок и приема символов. В [7 .1. 7 ] приведен код для процедуры приема данных. ;---ждем, пока все будет готово дл11 посЫJJки символа КЕТRУ: MOV DX,BASADR ;базовый адрес ADD DX,5 ;указываем на ре1'Истр статуса линии IN AL,DX TEST AL,000111108 JNZ ERRROUT ' TEST AL,00000001 В JNZ RECEIVE ТЕSТ AL,001000008 ;получаем байт статуса ;провер11ем на ошибку ;если есть, то на процедуру обработки ошибки ;проверяем, получены ли данные ;если да, то на процедуру приема ;проверяем 1urовность к передаче JZ КЕТRУ ;если нет, то возвращаемся назад ;---передаем СИМВWI, принимаемый с клавиатуры MOV АН,1 ;функци11 проверки нажатия клавиши INТ 16Н ;прерывание клавиа,:уры BIOS Ji КЕТRУ ;возврат, если не было нажатия MOV АН,0 ;функция i10луче11ия кода с клавиатуры iNТ 16Н ;теперь нужный символ в AL
Ввод/вывод 451 SUB DX,5 ;адрес реп1стра хранения 11ере;щт'l~1ка OUT DX,AL ;посылаем с~1мвол JMP SHORT KETRY ;возuращасмс>1 к на•~алу цикла 7.1 .7 . Получение данных Как только инициализирован коммуникационный порт (7.1 .2] и установлена связь с удаленной станцией (7 .1.5] - коммуникаци­ онная программа готова принимать данные. Прием данных никог­ да полностью не отделен от их передачи, поскольку программе может потребоваться послать сигнал XOFF (ASCII 19), чтобы оста­ новить поток данных, если они поступают слишком быстро и она не успевает их обрабатывать. Код XON (ASCII 17) сообщает уда­ ленной станции, что можно продолжить передачу. Отметим, что PCjr не· может принимать данные во время дисковых операций; чтобы снять это ограничение, нужно применять XON и XOFF. В зависимости от сложности протокола обмена принимаемые данн~е могут требовать простой или С{IОжной обработки. Может быть получен любой код из набора управляющих кодов, приведен­ ных в [7.1 .9]. Те из них, которые являются ограничителями дан­ ных, чаще обнаруживаются при синхронном обмене. При выводе получаемых символов на экран учитывайте влияние символов перевода строки (ASCII 1О), поскольку некоторые языки (включая Бейсик) автоматически вставляют их после каждого символа воз­ врата каретки; в этом случае исключайте символы перевода строки из . принимаемых данных, чтобы избежать пустых строк при выводе. На рис. 7.2 показана коммуникационная процедура, вклю­ чающая та1<же код передачи, обсуждаемый в [7.1.6 ].. - Высокий уровень Для коммуникационной процедуры, написанной на интерпре­ тируемом Бейсике, время очень существенно. Обработка осущест­ вляется очень медленно, поэтому если процедура приема неверно сконструирована, входной буфер может уже заполниться (т.е. про­ изойдет переполнение), в то время как программа еще будет ана­ лизировать ранее полученные данные. Очевидным решением этой проблемы является применение максимально возможного размера буфера. При загрузке Бейсика размер буфера ввода устанавлива­ ется добавлением к коман-де ключа / С:. BASICA / С: t 024 создает буфер размером в lK и это минимальное число, необходимое для скорости обмена 1200 бод (сложным процедурам может понадо­ биться 4096 байт). По умолчанию используется размер буфера, равный 256 байтам, и такей буфер имеет то преимущество, что он 15*
452 Глава 7 может быть целиком, помещен в одну символьную переменную. Такой размер буфера можно использовать только при скорости обмена не более 300 бод. Посыла-, XON Перерыв CBIIЭM Посыпаем XOFF Передаем сммвоn Рис. 7.2 . Основная процедура коммуникации Бейсик читает из буфера с помощью оператора INPUT$ (можно использовать также INPUТ-# и LINE INPUT #, но INPUT$ бол~е гибок). Этот оператор имеет форму INPUТ$ (числобайт,номерфайла). Например, INPUT$ (1О, # 1) читает 10 байт из коммуникационного канала, открытого как файл # 1. Если размер буфера не превышает 256 байт, то очень удобно
Ввод/вывод • 453 читать все содержимое буфера за один раз. LOC сообщает, сколько байтов данНЬiх находится в буфере в настоящий момент. Поэтому напишите оператор INPUT$(LOC(l),#l), и в S$ будут записаны все данНЬiе с момента последнего доступа к буферу. Конечно, если LOC(l) • О, то буфер пуст, и процедура должна ожидать, пока данные будут получены. Отметим, что EOFO) также можно использовать для проверки состояния буфера, так как эта функция возвращает -1, если буфер пуст, и О, если там есть хотя· бы один символ. После того как данные записаны в S$, программа должна nро­ верить" не содержатся ли там управляющие коды. Быстрее всего выполняет эту задачу функция INSTR. Напомним, что ее пара­ метрами являются, во-первых, позиция, с которой надо вести поиск в строке, во-втор!>fх, имя строки и, в-третьих, искомый сим­ вол (или строка). Чтобы найти символ XOFF. (ASCII 19), оператор должен иметь вид INSTR(l,S$,CHR$O9)). А чтобы найти второе появление нужного управляющего символа, повторите поиск в строке, начиная с символа. следующего за позицией, в которой найден первый. . Обычно процедура ввода исключает большинство управляющих символов из принимаемых данных, с тем чтобы они нормально выглядели при выводе. Затем данные выводятся на экран, пересы­ лаются в другое место в памяти, а иногда записываются на диск или выводятся на принтер. В процессе .всей этой деятельности программа должна постоянно контролировать ~оступление новых данных. Если оказалось, что буфер заполняется слишком быстро, то программа может послать сигнал XOFF, останавливая поток данных. Затем, после того как полученные данные будут декодированы, можно снова разрешить передачу данных. Конечно, необходимо, чтобы протокол обмена поддерживал XON ,и .XOFF. Обычно программы, написанные •на интерпретируемом Бейсике, могут использовать XON / XOFF для установления соответствия скоро...--тей при приеме данных, но при ' передаче данных такая программа часто не может достаточно быстро отреагирова·rь на получение сигнала XOFF. • 500 '"здесь находится процедура передачи (см. (7. 1.6)) 600 IF LOCШ>IOO ТНЕN XOFF 1: PRINT #l,CHR$(19) ·посылаем XOFF при заполнении буфера 610 С$ = INPUТ$(LOC(l),#l) ·ч,итаем содержимое буфера 620 "'выделяе~-, из данных управляющие символы
454 Глава 7 630 IF INSТR(.l,C$,CНR$(19))>0 THEN 800 640 IF INSJ'RO,C$,CНR$(17))>0 THEN 900 'получен XOFF 'получ~н XON (здесь удаляются ненужные уnрамяющие символы) 700 PRINT С$ выводим данные на экран 710 IF LOCO) > О THEN 600 'если получены данные, то читаем их 720 IF XOFF = 1 THEN XOFF О: PRINT #l,CHR$(17) 'выключаем XOFF 730 GOTO 500 'уход к началу процедуры передачи 800 'реакция на XOFF 900 'реакция на XON Если функция LOF применяется к коммуникационному порту, то она возвращает информацию о количестве свободноrо места, оставшемся в буфере ввода. Например, если СОМ 1 открыт как # 1, то LOF (1) -сообщит о количестве свободноrо пространства. Это может быть полезно для определения заполнения буфера. Отме­ тим, однако, что оператор LOC возвращает позицию указателя в буфере, и это значение может быть использовано для той же цели. Например, если COMl открыт как - #3, а размер буфера ввода равен 256. байтам, то до тех пор, пока LОС(З) не вернет 256, буфер не будет полным. Средний уровень Функция 2 прерывания 14Н BIOS ожидает символ из последо­ вательноrо порта, помещает ero· в AL при получении и затем возвращается в программу. При входе надо поместить номер порта (0-1) в DX. При возврате АХ равен нулю, если не было ошибки. Если АН не равен О, то может быть возвращен байт статуса, в котором имеют значение только 5 бит. Это следующие биты: бит 2 3 4 5 ошибка переhолнения (новый п1мвол поступил раньше, чем был удален старый) ошибка четности (вероятно, из-за проблем в линии) ошибка оформле11ия (стартовый или стоп-биты неверны) обнаружен перерыв (получена длинная строка битов 0) ошибка тайм-аута (не получен сигнал DSR)
Ввод/вывод 455 MS- DOS также предоставляет коммуникационную функцию для приема одною символа - фун.кцию 3 прерывания 21Н. Она ожидает символ из СОМ! и помещает его в AL. Отметим, что при этом нет функции инициализации порта, которую надо делать с помЬщью процедуры BIOS или непосредственно, как показано в (7 .1 .2 ]. По умолчанию порт инициализируется со значениями 2400 бод, нет контроля четности, один стоп-бит и 8 бит на символ. Эта функция не имеет никаких преимуществ по сравнению с функцией BIOS и не возвращает информации о статусе. Низкий уровень При получении данных без использования коммуникационного прерывания [7 .1 .8 ) программа должна постоянно проверять реmстр статуса линии, адрес порта которого на 5 больше базового адреса используемого коммуникационного адаптера. Бит О этого реmстра будет равен нулю, пока не будет получен символ в реmстр данных приемника. Когда бит О становится равным 1, надо немедленно считать ero из регистра, с тем чтобы на него не нало­ жился следующий принимаемый символ. После того как символ считан, бит О опять становится равным О и остается таковым, пока не прибудет новый символ. • Хотя об этом еще не упоминалось, но вы должны знать, что коммуникационные процедуры обычно создают циклический буфер для сбора поступающих символов. Циклические буфера обсужда­ лись в [3.1.1 ]. Вы должны также знать, что если поступающие данные подавать на экран со скоростью 1200 бод, - то процедуJk1 сдвига экрана BIOS (4.5 .l) не будет успевать и произойдет пере­ полнение. Простое решение этих проблем состоит в использовании коммуникационного прерывания, как объяснено в [7 .1.8 ]. В следующем примере частично повторяется содержимое пре­ дыдущею .параграфа, относящегося к передаче символов. Как и в том случае, код начинается с бесконечного цикла. Объедините эти две процедуры с процедурами инициализации из (7 .1.2] и [7 .1.5] для создания законченной процедуры ввода/ вывода через комr:~у­ никационный канал. КЕТRУ: MOV DX,BASADR ADD DX,S IN AL,DX ТЕSТ AL,000111108 JNZ ERRROUT ;базовый адрес ;указываем на регистр статуса линии ;получаем байт статуса ;проверяем на о~ш1бку ;если да, то на обработку ошибки TEST AL,00000001 В ;проверяем, nолу•аены ш1 данные JNZ RECVE ;на процедуру приема данных ТЕSТ AL,001000008 ;проверяем готовность к передаче •
456 JZ КЕТRУ ;если нет, то к началу цикла (здесь расположена процедур~ передачи - см. (7. l .6)) ;---получаем данные и выводим их на экран . RECVE: MOV DX,BASE_ADDRESS ;базовый адрес 1N AL,DX ;ч11таем полученный символ СМР AL,19 ;проверка на XOFF JE XOFF_ROUTINE ; (и т.д.) ' MOV DL,AL • ;готов~1м символ для вывода на экран MOV АН,2 ;функция вывода символа INT 2lH ;выводим его JMP SHORT КЕТRУ;возвращаемся к началу цикла 7.t.8 . Пdсыnка/nоJ1ученне даннь1х с помощью коммуннкацнонн~rо прерывания Глава 7 Тщательно продуманная коммуникационная программг. имеет слишком мноrо обязанностей, чтобы посвятить себя целиком вво­ ду /выводу. Поступающие данные должны анализироваться, пере­ даваемые данные должны собираться, а большие блоки данных могут записываться на д\lСК или считываться с него. J(оммуника­ ционное прерывание позволяет программе не тратить на ввод/ вы­ вод больше времени, чем он тоrо требует. Например, после уста- 1новки прерывания управление передается процедуре передачи дан­ ных только в том случае, если регистр хране'1ИЯ передатчика пуст, и возвращается программе, как только послан байт данных, позво­ ляя ей продолжать свою работу до тех пор, пока регистр хранения передатчика не будет снова rотов. Не забудьте ознакомиться с об­ суждением прерываний в (1.2.3 ), прежде чем продолжить чтение. IBM РС отводит два· аппаратных прерывания для коммуника­ ционных каналов, номера 3 (COMI) и 4 (СОМ2). Отметим, что у PCjr встроенный модем имеет номер 3, а COMl - номер 4. Микро­ схема ЦАR Т 8250 допускает 4 класса прерываний для каждого канала, используя следующие двоичные кодовые числа: 00 изменение в регистре статуса модема 01 регистр хранения передатчика пуст 1О получены данные 11 ошибка приема, или получено условие перерыва
Ввод/вывод 457 Эти коды содержатся в битах 2-1 регистра идентификации пре­ рывания, адрес порта которого на 2 больше, чем базовый адрес используемого коммуникационного адаптера. Бит О этого регистра устанавливается при возникновении прерывания, а остальные биты не используются и всегда равны О. ' . Чтобы выбрать одно IСЛИ более прерываний, надо запрограмми­ ровать регистр· разрешения прерывания, адрес которого на 1 больше базового адреса. Значения его битов могут быть такими: бит О = прерывание при получении данных 1 = прерывание, когда pentcтp хранения передатчика пуст 2 = прерывание при ошибке приема данных 3 = прерывание при изменении регистра статуса модема 7-4 не используются, всегда О Когда одно.· из этих событий происходит, инициируется аппа­ ратное .прерывание, возникающее в микросхеме обработки преры­ ваний 8259 по каналу 3 для COMI и по каналу 4 для СОМ2. Про­ цедура обработки прерываний передает управление тому коду, на который указывает соответствующий вектор прерыва11ия. Пос­ колыr.у это аппаратное прерывание, то оно может быть маскиро­ вано (см. (1.2.2)). Пом~ите, что процедура обработки прерывания должна завершаться стандартным кодом выхода из аппаратного прерывания MOV AL,20H/OUT 20H,AL. На рис: 7.3 показано коммуникационное прерывание. Одновременно возможн;о любое число типов прерывания. Но если разрешен более чем один тип, процедура обработки прерыва- . ния должна сама определять, какой рз типов прерывания :..1роизо­ шел, проверяя регистр идентификации прерывания. Одновременно • могут происходить более чем одно прерывание; поэтому бит О регистра идентификации ''сообщает о том, что поступило еще одно прерывание. Когда два или более прерываний поступают· в один и тот же_ момент,· то они обрабатываются в порядке, указанном в приведенной ниже таблице. Добавочн111е прерывания должны быть обработаНЬJ до завершения процедуры обработки прерывания. Условия предшесrвующих прерываний "отменяются" с помощью дейсrвий, предлагаемых в правом столбце следующей таблицы: Ко.а !!ш действия мя "сброса" 11 ошибка или перерыв чтение реmстра статуса линии lО получены данные чтение pentcтpa •приемника данных 01 передатчик готов вывод симВОJiа в· pentcтp хранения передатчика· 00 изменение статуса модема чтение ре.,..стра статуса модема
458 INТОВН Коммун11кацмонн111 процедура ВвоА, ан11111э, 31110- м11нанме II вывод А8ННЫХ "Поаnроrрамма Буфер Буфер npepwвaнМII" вывор ввода г------- :-t-- -----{-: --- 11 11 1 1 1 1 1.. 1 1 1 1 1 1 1 1 1 1 1 Анаnмэ тмnа npe~вa­ HМII 11 11 11 11 11 11 11 11 11 1: Первааn11к готов - 11ОСЫ11НМ байт Даннwе nJ)Мllllты - ЗIIIOМllltaeМ байт 11 Изменение статуса мо..ема • - проверка II восстаноменме Усповме nрерwван1111 - прекращаем CIII~ 1 Обнаружена оwмбка Нет 1 - 80CCТIНOINl8НIМI 1· --------- 1 -L ---------- ------- дё..J. Рис. 7.3 . Коммуникациоi,1Ное-прерывание Низкий уровень Глава 7 IREТ Приведем общую форму программы., обрабатывающей комму­ никационные прерывания:
Ввод/вывод ;---установка вектора коммуник~ционноrо 11рер1,ша11и11 PUSH DS ;сохраняем DS MOV DX,OFFSEТIOIN ;DS:DX указывают на r1роцедуру MOV AX,SEG IOIN MOV DS,AX MOV AL,0BH ~омер вектора для СОМ! MOV АН,25Н ;функциs, изменения вектора INТ 21Н ;мен11ем вектор прерывани11 ;---инициализаци11 реrистра_разрешени11 преры~ния (СОМ!) MOV АХ,40Н · ;DS указы!13ет,на данные BIOS MOV DS,AX. MOV ·DX,DS: [О] INC DX MOV AL,3 OUT DX,AL ;получаем базовый адрес СОМ! ;указываем на регистр разрешения ;прерываний и разрешаем прерывания ;приема и передачи РОР DS ;восстанавливаем регистр ;---процедура об~ботки прерывания - сначала определяем его тип IOIN PROCFAR NEXIN: MOV DX,BASADR INC DX INC DX IN AL,DX. TEST AL;I0B JNZ ТRANSMIТ RECEIVE: ;базовый адрес ;указываем на регистр идентификации ;црерывання ;читаем ero значение· ;это прерывание n_ередатчика? ;если да, то на передачу ;иначе - на прием \ 459 JMP SHORT ANOT ·ТRANSMIT: ;проверяем, нет ли дpyroro прерывания ;начинаем процедуру ~;~ередачи символа I ;---перед выходом проверяем,нет ли дpyroro прерывания ANOT: . MOV DX,BASADR ;базовый адрес INC DX ;указываем на регистр идентификации INC ЬХ ;прерывания IN AL,DX ТЕSТ AL,l JNZ NEXT_INТ MOV AL,20H ;читаем el'O значение ;проверяем бит 1 ;если он установлен, то на начало ;иначе,код завершения аппаратного
460 OUT 20H,AL IRET IO_INT ENDP ;npept,IIШIIHЯ 7.1 .9 . Сводка управnяющих кодов, испоnьзуемых при . комму никаци и . Глава 7 В таблице 7.1 приведены 32 управляющих кода ASCII, которые используются при коммуникации, а также при работе принтера и других устройств. Добавлен также код ASCII 127 - DEL ("забой"), который является управляющим кодом, хотя его и нельзя выдать с клавиатуры с помощью сочетания Ctrl + клавиша. Применение некоторых кодов, таких, как возврат каретки, инвариантно. Но большинство друmх управляющих кодов имеет ,широкий диапазон интерпретации во 11,Jногом из-за несовместимости оборудования. 7 .2. Соэданне драйвера устройства Драйвер устройства - это специальная программа, которая управляет обменом с периферийным устройством, таким, как принтер или дисковый накопитель. Поскольку параметры этих 'периферийных устройств меняются в зависимости от производи­ теля, то разным пользователям программы может потребоваться дюжИНЭ\ различных драйверов, чтобы он мог работать на имею­ щемся у него оборудовании. Имеется 4 способа включения драй­ веров устройств в программу. 1. Можно поместить код для всех драйверов непосредственно в программу. Например, чтобы поддерживат1, различные принтеры, можно создать таблицу управляющих последовательностей и искать в ней нужный код каждый раз, когда он потребуется. Этот подход тратит много памяти и может быть достаточно медленным. 2. Создать ряд драйверов устройств и потребовать, чтобы прог­ рамма загружала необходимый в качестве оверлея (т.е. помещать его в область программы, специально оставленную дл~ этой цели (см. [1.3 .5 ])) . 3. Создать драйвер устрсйства как отдельную программу, кото­ рая указывается в командном файле, выполняемом при загрузке системы. Программа запускается и устанав.,1иваст драйвер уст­ ройства как программу обработки прерывания. После ::>того прог­ рамма завершается, но остается резидентной в памяти, как объяс-
Ввод/вывод 461 Номер КОА8 ASCII ~•м- Таблица 7.1 111-ii 16-м ~ cCtrl нмка Наэна'1811118 00 00 (nuU) "@ NUL C11м10J1-paJAe1111te11ь (не 11ме1ОЩ11ii эна..ен1111; нео6хоА11м AJIII Э8j18ржек). 01 01 "А SOH Н8'18110 эаrоnоека. Н8•11нает ПepeAa'l'f~ока,j18ННЫХ 111111 ноеого фaiilla. 02 02 • 2'"8 STX Ha•ano текста. 0тме"ет на'1811о текста, CJ111AYioщero эа эаrоnоеком А8ННЫХ. 03Э3• '"С ~х Конец текстL Может отме"ть на'1811о А8ННЫХ при n~ еерке оwмбок. 0404♦ '"D ЕОТ Конец nереаа•м. КоА останоекм, но 11НОГА8 он nросто отме•ает конец фaiilla. 0505• "Е ENQ Запрос. Заnраwмвает статусну11 мнформац111О у отааnен• 0606• "F АСК СТ8НЦ1111. , ПоАТНРQ8Н118. ПоАТНР*А88Т ycnewныii обмен МРАУ СТ8НЦ1111М11. 07 07 "G BEL Заонок. Ин11111111рует .-ок, .тобw nрм8118.ь внимание. 0808• '"Н BS Воsарат на war• 0909о "1 нт Гормsонтаnьнм та6у11,щм11. 10 ОА • "J LF Перевод строки. 11 овd "К vт Верт11каnьн111 та6уn11Ц1111. 12 ос; "L FF Пере,оА формата. 13 0D .,. ' "М CR Воsарат каретки. 14 ОЕ 1" "N so САамГ аыкn11•ен. Перекn11чает набор сммаоnоа. •15 0F~ '"О SI САамг акnmен. Перекn~о•ает Н!lбор сммаоnоа. 1610'► '"Р DLE Data Llnk Escape. МоАмф111111рует эна..енме CJleдylOЩIIJC с11м1ОJ1оа (анаnогм•но Esc). 17114 "Q DC1 Уnрааnенме _устроiiстаом 1. Исnоnьэуете11 как смгнаn XON "R удаnенноii станцмм на nереаачу. ' 18 12 DC2 Уnрааnенме устроllством 2. Смгнаn ne~meн1111 общего наэна..ен1111. 1913!1 '"S DC3 Уnрааnенме устроiiстаом 3. Исnоnьэуетс11 как ~игнаn XOFF уАадениоl\ станции..,.,. nрекращен1111 nepe,!18'111. r20 14 11 "Т DC4 Уnрааnенме устроiiстаом 4. Смгнаn nepeкnmeн1111 общеr9 Н83Н8'18Н1111. 21.15§ "U NAK Отрицание. Перем•• неусnеwна•. 22 16 V SYN Промежуток с11ихрон1111111111. ИсnО11ьауете11 межАУ бnокам11 при смнхронноii с111э11. 2317l. "W· тв Конец бnока nереАачм. Вариант ЕТХ. 24 18 "Х CAN Отмеиа. Обычно сигнаnмэмрует об оwмбк1111ерцач11. 25 19 у ЕМ Конец с~- С11гнаn11111рует о фмэическом конце 11сто•ника j18ННЫХ. 26 1А "z SUB ПоАСТ8НО8КL Эамен11ет·оwмбочные сммаоnы, а также те, "[ ESC которwе Н88ОJМОl(НО мэобраэмть. • 27 1В Отмечает nосnеду1ОЩ11е сммаоnы как ynpaan11ioщyio nocne- A08aTeRltHOCTb. 28 1СL ,./ FS Рщеn11tе11ь фaiinoa. Отмечает nогическу~о границу между фallnaм11. ' 29 1D ") GS Paцen11te111t групп. Отмечает norи'll!cкyio границу между ,.,. гpynnai.411 А8ННЫХ. 301Е.. RS PщenllteJlь записей. Оте-т nогмческу~о границу "4811АУ эanмCIIMII j18ННЫХ. 31 1F А .. _, us Рщеn11tе11ь объектое. Отме"ет nor11'18CК'fl0 границу ме•АУ обИКТаt.411 А8ННЫХ, 1277FQ нет DEL Забоll. УАад118Т АРУГ118 С11"480'1Ы,
462 Глава 7 нено в [1.3 .4 ]. Впоследствии наша программа испол1,зует этот драйвер через вектор прерывания. 4. Создать полноценный драйвер устройства, который будет загружаться при старте с помощью файла CONFIG.SYS: MS-DOS поддерживает такой тип драйверов устройств и однажды загружен­ ный он может использовать все возможности команд DOS, вклю­ чая проверку ошибок. Специальная команда IOCTL (контроль вво­ да/вывода) позволяет программе узнать статус драйвера и послать ему управляющую строку помимо обычного потока данных. Первые три способа легко реализовать с помощью информации, приведенной в остальных частях данной книги. Устанавливаемые драйверы устройств очень сложны, зато когда они имеются - у вас есть хороший помощник. В этом случае система будет работать с устройством настолько же тесно, как с клавиатурой или дисковым накопителем. Устройству может быть присвоено имя, например, SERIALPR для последовател1,ного принтера, и затем оно может быть открыто для доступа из любого языка. В Бейсике оператор OPEN "SERIALPR" FOR OUTPUT AS #2 подготовит последовател~,ный пр11нтер для вывода. В языке ассемблера вы сможете получить доступ к принтеру как с помощью метода управляющего блока файла, так и с помощ1,ю метода дескриптора файла, включщ1 очен1, мощную функцию IOCTL. При этом пользователь имеет возможность доступа к устройству на уровне операционной системы и может просто ввести команду СОРУ A:MYFILE SERIALPR:, чтобы скопировап, содержимое файла на принтер. ' Устанавливаемые драйверы устройств могут быть написаны только на языке асссмблер"а и обслужнвап, два типа устройств: символьные и блочные. Эти имена оn11сывают ед11н11цы, которымн устройство обрабатывает данные. Обычно драiiверы блочных уст­ ройств обслуживают дисковЬ1е накопите,111, а драiiвсры симво,11,ных - все остальное, начиная от последовател1,ных принтсрон 11 кончая роботами. Блочные устройства обменнваются блокам11. данных, поэтому они занимаются накоплением данных. С11мвол1,ные уст­ ройства обмениваются данными побайтно, по:::пому он11 лучше под­ ходят для управляющих устройств, а также д,1я устроiiств, которые нс могут обеспечить высокую скорост1, обмена данн1,1м11. Драйверы блочных устройств очею, сложны 11 здес1, нет достаточно места, чтобы объяснить их структуру. Редко кому требуется написать такой драйвер. Техническое руководство по MS-DOS предоставляет всю необходимую информацию II содсржнт полный пример драйвера виртуального диска в опсрат11вно1·1 памят11. Вы можете просмотреть эту информацию, пос1с того к;1к 11зуч1пс обсуждение драйверов С11МВОЛl,НЫХ устройств, ПJ)IIBC,J.CHHOC З,J.CCI,.
Ввод/вывод 463 Устанавливаемые драйверы устройств беспощадны к ошибкам программистов. Поскольку драйверы автоматически загружаются системой при загрузке, то невозможно использовать отладчики для выявления причи~ неполадок. Поэтому будьте предельно внима-' тельны при их написании. Программа драйвера устройства разбивается на три части, каждая из которых обсуждаеrся отдельно в следующих параграфах. Это ( l) заголовок. драйвера, который именует устройство и содержит информацию об остальных частях драйвера, (2) стратегия драйвера, которая хранит информацию об области данных, создаваемой MS-DOS, которая называется заголовком запроса, и (3) обработчик прерывания устройства, ·который и содержит код, управляющий устройством. 7.1 .t . Создание заrоnовка драйвера ' Драйверы устройств должны создаваться в виде СОМ-файлов [l.3.6 ]. Однако они не являются нщтоящими программами, пос­ кольку у них отсутствует префикс программноrо сегмента. Чтобы добиться этого, не надо включать оператор ORG lOOH в начале программы, как это делается для сом:..фаЙЛОfl. Либо запишите ORG О, либо вообще ничеrо не пишите. Драйвер должен быть опи­ сан как далекая <lar) процедура, как и в любой программе. В сле­ дующем примере приведен начальный код для драйвера устройства с именем DEVICE12. Оно заменяет стандартное устройство AUX, используемое MS-DOS, принимая вывод функции 4 прерывания 21Н. Весь драйвер устройства состоит из кода этоrо раздела вместе с кодом, приведенном в следующих двух разделах; поместите их подряд один за другим, чтобы получить полную программу. Драйвер устройства должен начинаться с заrоловка драйвера. Он имеет длину 18 байт, разделенных на 5 полей. Первое поле (DD) всегда содержит значение -1 (FFFFFFFFН), и когда MS-DOS загружает драйвер, то оно заменяется на стартовый адрес следу­ ющего драйвера. Таким образом, система может искать следующий драйвер по цепочке. У последнего загруженноrо драйвера в этом поле остается значение -1. Второе поле это байт атрибутов драйнера. Имеют значение только 7 бит: бит 15 = симВОJiьное устройство. О = блоч11ое устройство 14 = поддерживает IOCfL, О = не поддерживает IOCТL 13 = формат блоков IBM, О = друrой формат блоков 3 часы, О = не часы 2 = нулевое устройство, О = не нулевое устройство
464 1 о устройство, стандартного вывода, О = нет устройство стандартного ввода, О = нет Глава 7 Обычно установлен только бит 15, или биты 15 и 14, .если уст­ ройство поддерживает IOCTL (как обсуждается в (7.2 .4 ]). Бит 13 устанавливается только для блочных устройств. Остальные биты используются для замены устройств, включаемых MS-DOS по умолчанию (устройствами стандартноrо ввода и вывода являются клавиатура и видеодисплей; устройство часов объединяет часы реальноrо времени с часами времени суток BIOS; а нулевое уст­ ройство (NULL) - это псевдоустройство, используемое для • т~­ товых целей). Третье и четвертое поля содержат смещения для процедур стратегии и обработки прерывания, которые будут рассмотрены в следующих параграфах. Наконец, последнее поле содержит имя устройства. Имя может содержат~ до 8 символов, и оно должно быть выравнено по левому краю с завершающими пробелами. Для замены существующих в DOS устройств, таких, как LPTl ИЛИ COMl, надо указать то же имя устройства, как в данном примере. Низкий уровень В данном примере создается драйвер для последовательнQrо устройства. "DEV12" - имя файла, который должен быть указан в файле конфигурации сиситемы, чтобы этот драйвер был загружен. В байте атрибутов установлен только бит 15, указывая, что это символьное устройство и что оно не поддерживает IOCTL. DEV STRATEGY и DEV INTERRUPT ... имена ri,роцедур, обсуж­ даемых в следующих параграфах. Устройство названо AUX, с тем' чтобы заменить обычное устройство MS-DOS с этим именем. Это позволяет очень просто обращаться к .этому устройству, поскольку система имеет предопределенный номер файла для обращения к устройству AUX (последовательному). В пример включен началь­ ный код для драйвера, определяющий ero как СОМ-программу. CSEG SEGMENT PUBLIC 'CODE' ;устанавшшаем кодовый сеrме1п ORG О ;эта строка необязател~;на ASSUME CS:CSEG,DS:CSEG,ES:CSEG DEVl 2 PROC FAR ;драйвер это далекая процедур., DD 0FFFFFFFFH DW 8000Н DW DEV STATEGY DW DEV_INTERRUPT DB 'AUX ;адрес СJ1едующеrо драйвер., ;байт атр•1бутоп ;адрес процедуры стратеr•1и ;адрес процедуры прер1,шаш1я ;имя устройст11а (до1юл11е111юе нробелами)
Ввод/вывод 465 7.1 .1 . Создание стратегии устройств.а Для процедуры стратегии устройства необходимо только пять строк. Когда система загружает устройство, она создает блок дан­ ных, называемый заголовком зanpQCa. Он имеет две функции. Во­ первых, он служит областью' данных для внутренних операций системы. Во-вторых, что более важно, заголовок запроса служит ' областью, через которую происходит обмен информацией между драйвером и вызывающей его программой. Например, когда драй­ вер выводит данные, адрес данных он получает через заrоловок запроса. Когда же драйвер завершает свою работу, он устанавли­ вает в заголовке запроса 'байт статуса, который доступен вызыва­ ющей программе, тем самым давая возможность ей узнать об _ошибке. 1 . . . MS- DOS создает заголовок запроса при установке драйвера уст­ ройства (когда система загружается). В этот момент (и только один раз) выполняется процедура стратегии устройс~ва. При этом ES:BX указывают на вновь созданный заголовок запроса и про­ цедуре нужно просто скопировать их, чтобы впоследствии он мог быть обнаружен при обращении к драйверу. Адреса смещения и сегмента. заголовка помещаются в две переменные. В следующем разделе вы увидите, что при обращении к драйверу, первое, что он делает - восстанавливает значения ES:BX, чтобы можно был'о получить и~формацию из заголовка запроса. Размер заголовка запроса может меняться, 1в зависимости от типа запроса к драйверу (например, инициащ:1зация, вывод данных или возвр'ат статуса). Однако первые 13 байт заголовка всегда одни и те же. Приведем их формат: 1. Длина заголовка запроса (DB). 2. Код устройства (DB). Определяет номер для блочНЬiх уст­ ройств. 3. Код команды (DB). Здесь хранится номер последней послан­ ной драйверу команды. Эти коды перечислены: в [7 .2 .3 j, 4. Статус (DW). • Статус устанавливается каждый раз при вызове .драйвера; Если установлен бит 15, то в младших восьми битах находится код ошибки. Коды ошибок пере­ числены в [7.2.3 ]. 5. Резервная область (8 байт). Используется MS-DOS. 6. Данные, необход}fмые для работы драйвера (переменной длины). (
466 Глава 7 Низкий уровень Ниже представлены 5 строк процедуры стр:1теrии устройства. Отмечаем, что две словные переменные, хранs~щие значениs~ ES и ВХ, следуют за инструкцией RET, как и положено в формате сом. DEV_SТRATEGY: MOV CS:KEEP_ES,ES MOV CS:KEEP_вх.вх RET КЕЕР CS DW ., КЕЕР ВХ DW ., 7.1 .3 . Соэдание обработчика прерывания устройства Драйвер устройства начинаете~ с двух порций кода, описанных ранее. За ними должна следовать соответствующая процедура об­ работки прерывания. На самом деле неверно назынат,, эту проце­ дуру процедурой обработки прерывания, так как она новее нс обс­ луживает прерывание и завершается обычной инструкцией RET. Имеет.ся 13 типов функций, которые может н1,шолнять уста­ навливаемый драйвер устройства. Когда драйвер вызывается функ­ цией DOS (скажем, функцией 3FH прерывания 21 Н, которая читает данные из файла или устройства), то функциs~ помещает кодовый номер от l до 13 в однобайтовое поле по смещению 2 R заголовке запроса (для ввода - кодовый номер 5). Затем управ­ ление передается процедуре обработки прерывания драйвера, адрес которой определяется при просмотре заголовка драйвера [7.2 .l ]. Эта процедура в первую очередь восстанавливает ES:BX, с тем чтобы они указывали на заголовок запроса, а затем читает кодовый номер команды. По этому коду процедура обработки пре­ рывания вызывает нужную процедуру, которая выполнит требу­ емую функцию. Процедура ищется с помощ1,ю 13-слонной таб­ шщы, содержащей смещения для 13 типов функций. Функции всегда перечислякхся в следующем порядке: 1. INITIALIZE (иниц~~аш1зацш1) 2. CHECK_MEDIA (проверка 1юсюсJ1>1) 3. МАКЕ_ВРВ (блок параметров диска) 4. IOCTL_IN (контроль ввода/оывода - оход) 5. INPUT_DATA (ввод данных) 6. NONDESТRUCT IN 7. INPUT_STATUS (статус ввода) 8. CLEAR _INPUT (о•~истка ввода)
Ввод/вывод 467 9. OUTPUT_DATA (вьшод данных) 1О. OUTPUT_ VERIFY (проверка вывода! 11. OUTPUT_STATUS <статус вывода) 12. CLEAR_OUTPUT·(o•шcткa вывода) 13. IOCTL_OUT (контроль 11nода/ш,11юд;:1 - 111,,хо;() После выполнения задания процедура обработки прерывания завершается инструкцией RET, и управление возвращается в вызывающую программу. Драйвер устройства может включать код для обработки только некоторых функций в з.ависимости от уст­ ройства и требуемой степени контроля ошибок и управления уст­ ройством. Номера функций, для которых нс написаны процедуры, должны завершаться ·выходом из драйвера без выполнения чеrо­ либо. В этом случае надо только перед выходом установит~, биты 15, 8, 1 и О в -заголовке запроса, чтобы информировать вызыва­ ю~ую программу, что была затребована несуществующая функция (бит 15 индицирует ошибку, бит 8 показывает, что драйвер работает нормально, а биты О и I дают код ошибки 3, что соответствует "неизвестной команде"). Но одна функция должна присутствовап, во всех драйверах устройств - функция 1 (инициализация). Эта функция автомати­ чески выполняется только при загрузке драйвера. Одна из важных задач, выполняемая этой процедурой, состоит в установке адреса конца драйвера в четырех байтах, начинающихся со смещения 14 в заголовке запроса ..В приведенном ниже примере конец прог­ раммы отмечен меткой еор:. Кроме :пой з~щачи, процедура иници­ ализации должна также выпQлнип, всю необходимую для данного устройства инициализацию. На рис. 7.4 показана структура драй­ вера устройства. Какие из оставшихся 12 функций будут включены в драйвер устройства зависит от тоrо, что должен делап, драйвер. Некото­ рые, такие, как СНЕСК MEDIA и МАКЕ ВРВ, относятся только к блочным устройствам <они устанавливают тип диска, размер сек­ торов и т.д.). Для символьных устройств наибо.1сс важными явля­ ются две функции: INPUT DA ТА и OUTPUT DA ТА (от!',Jетим, что эти имена несущественны - важна ПОЗИЩIЯ в та&~ице фун­ кций, которая неизменна). В обоих случаях заголовок запроса имеет следующую структуру: 13 байт 1 байт 4 байта 2 байта 2 байта стандаi!fный формат за~-01ювка за11роса байт описа111-1я среды <тою,ко ;111>1 бло'l111,1х yиroi·н:пiJ смещение/сегмент буфера обме11а ;1а11111,1х число байтов, которое надо 1н:рещ1П, стартовый 1юмер сектора (тою,ко ;\ли бло•1111,1х)
З , 1 1 е с ь н а ч а л о И щ е м с т в о • З а r о n о в о к • д р а й в е р а С т р а т е г и 1 1 - у с т р о й с т в а О б р а б о т ч и к п р е р ы в а н м у с т р о й с т в а . , . > : : : : 1 . А д р е с с п е д у ю щ е г о у с т р о й с т в а 2 . А т р и б у т ы З . С м е щ е н и е д n 1 1 с т р а т е г и и П о n v , е н м е 1 1 А Р 8 С 8 4 . С м е щ е н и е д n 1 1 n р е р ы в а н м 5 . И М f l у с т р о й с т в а 3 8 Г О J I О В К а з а n р о с а о П о n у ' 1 8 е М К О , 1 1 к о м а Н , I I Ы У к а з а т е n ь н а з а г о n о в о к з а n р о с а - П р о с м о т р в , - - - . И н и ц м а n и э а ц М I I т а б n и ц е , - ~ • S ф у н к ц и й " " ~ - с ; " ' • П р о в е р к а с р е А ы Н е н а й д е н а ! ' i ~ . . . , е . l У с т а н о в к а с n о в а с т а т у с а Н а й , 1 1 е н а ! ~ - 1 1 В ы п о n н 1 1 е м п р о ц е д у р у Q . . . ~ t 1 - - - - - У с т ' 8 н о в к а с n о в а с т а т у с а ! & . 8 . t с : > 1 1 З а в е р w е н м е ! t R Е Т Р и с . 7 . 4 . Д р а й в е р с и м в о л ь н о г о у с т р о й с т в а З а r о п о в о к s а п р о с а ( с Q З А а е т е 1 1 0 0 S ) 1 . Д n и н а з а r о п о в к а 2 . K q у с т р о й с т в а З . K q к о м а Н , I I Ы 4 . С n о в о с т а т у с а 5 . Р е 3 е р е н u о б л а с т ь 6 . Д а н н ы е ~ ° ' 0 0 ; ; ' ~ t i : I О , ) - . . . J
Ввод/вывод 469 В приведенном ниже примере используетс:я функция в.ывода. Процедура, выполняющая вывод, получает из заrоловка запроса адрес буфера, в котором находятся выводимые данные (смещение 14). Она также считывает число байтов, которое надо вывести (смещение 18).. Когда процедура завершит вывод данных, она установит слово статуса в заrоловке запроса (смещение 3) и воз­ вратит управление. Если операция .завершится успешно, то надо установить бит 8 слова статуса. Друmе возможности будут обсуж­ дены позднее. Низкий уровень В данном примере приведена общая форма процедуры обра­ ботки прерывания, не включая реального кода, управляющего уст­ ройством. ;---инициализация обработчика прерыван~111 устройства DEV_INTERRUPt: PUSH ES ;сохраняем реn-~стры PUSH DS PUSH АХ PUSH ВХ PUSH СХ PUSH DX PUSH SI PUSH Dl PUSH ВР MOV AX,CS:KEEP _ES MOV .ES,AX MOV BX,CS:KEEP_:,ВХ MOV AL,ES: [ВХ] + 2 SHL AL,l SUB АН,АН LEA DI,FUNCТIONS ADD DI,AX JMP WORD РТR [DI] FUNCТIONS LAВEL WORD DW INITIALIZE DW CHECK_MEDIA DW МАКЕ_ВРВ DW . IOCТL IN. DW INPtJ(DATA DW NONDESТRUCТ_IN DW INPUT_STATUS DW C~AR_INPUТ ;ES:BX указывают на заrолоuок запроса ;получаем код комшщы из заголовка ;умножаем на 2 (так как таблица слов11а11) ;обнуляем АН ;DI указывает на смещение до таблицы ;добамяем смещение о таблице ;переходим 11а адрес из таблицы 1;это таблица функций
470 DW OUTPUT_DATA DW OUTPUT_VERIFY DW OUTPUT_STATUS DW CLEAR_OUTPUT DW IOCТL_OUT ;---выход из драйвера, если функция не поддерживается СНЕСК MEDIA: МАКЕ ВРВ: IOCТL_IN: INPUT_ DAТА: NONDESТRUCT_IN: INPUT:....STATUS: CLEAR_INPUT: OUTPUT_ VERIFY: OUTPUT_STATUS: CLEAR_OUTPUT: IOCTL_OUT: OR ES:WORD РТR [ВХ) + З,810ЗН ;мод~1фицируем статус JMP QUIT ;на запершеш1е программы ;---процедуры для двух поддерживаемых кодо11 Глава 7 INIТIALIZE: LEA АХ,Е_О_Р ;смещение ко1ща проrрнммы в АХ МОУ ES:WORD PTR [ВХ) + 14,АХ ;помещаем его в за~'f>Jю1юк MOV ES:WORD PTR [ВХ) + 16,CS ;rюмещасм сс1·ме1п ко,ща программы (здесь идет инициализация устройспш) JMP QUIТ OUTPUT_DATA: MOV CL,ES: [ВХ) + 18 ;rюлу•шем ч~1с;ю с~1м1юло11 CBW СХ ;СХ используем как счетчик MOV AX,ES: [ВХ] + 16 ;получаем адрес буфера данных МОУ DS,AX МОУ DX,ES:[BX] + 14 (здесь идут операции по выводу) JMP QUIT ;---выходим, модифицируя бМп статуса в заюловке запроса QUIТ: OR-ES:WORD PTR [ВХ) + З,lООН ;устанавливаем бит 8 РОР ВР ;восстанаплшщсм реп1стры РОР DI РОР SI РОР DX РОР СХ РОР ВХ РОР КХ
Ввод/вывод РОР DS РОР ES RET Е_О_Р: DEVICE12 ENDP CSEG ENDS END DEVICE12 471 ;метка конца программы ','' ' Перед возвратом драйвер устанавливает слово статуса в заго­ ловке запроса. В предыдущем примере это делается в двух местах, в зависимости от того, вызывалась функция, обеспечиваемая драй­ вером или нет. Эти строки выглядят так: OR ES:WORD PTR [ВХ] + 3,ХХХХН. Зн~чения битов ХХХХ следующие: биты 0-7 8 9 10-14 15 код ошибки (если бит 15 = 1) устанавливается в 1, когда функция завершена устанавливается в 1, крrда драйвер занят зарезервированы MS-DOS устанавливается при возникновен~1и ош_ибки В случае установления бита 15, индицирующеrо ошибку, млад­ ший байт этого слова содержит следующие коды ошибок: о 1 2 3 4 5 6 7 8 9 А в с попытка записи на защнще11ное от з.·шиси устройство неизвестное устройство устройство не rотово • неизвестная команда ошибка проверки по контрольной сумме неверная длина запроса к устройству ошибка поиска неизвестныr1 носитель сектор не найден нет бумаги в принтере ошибка записи ошибка чтения общая ошибка 7.1 .4 . Доступ к драйверу устройства Драйвер устроjiства устанавливается путем включения имени готовой пчаграммы в файл конфигурации системы. Для установки пробной программы поместите в файл CONFIG.SYS строку DEVICE • DEVICE12.COM. Затем перезагрузите систему для установки драйвера. Если машина не будет загружаться, то скорее всего имеется ошибка в коде инициализации драйвера.
472 Глава 7 После тоrо как драйвер установлен, для доступа к нему поль­ зуйтесь обычными функциями прерывания 21.Н MS-DOS. Какие понадобятся функции зависит от тоrо, заменяет ли устройство стандартное устройство DOS (как в приведенном примере) или оно добавляется как совершенно новое. Для замены стандартного пос­ ледовательного устройства назовите драйвер AUX, после чеrо функции 3. ((7.1.7]) и 4 ((7.1.6]) прерывания 21Н будут осущест­ влять соответственно ввод и вывод. Если устройство параллельное, то назовите ero PRN, после чеrо функция 5 (6.3.1] будет выводить данные на принтер. Друrой возможностью является применение функции· 3FH ( (5.4.4 ]) для ввода и 40Н ( (5.4 .3 ]) для вывода. В этом случае используйте номер файла 3 для последовательного устройства и 4 для параллельного. Напоминаем, что при работе с предопределенными номерами файлов нет необходимости отк­ рывать устройство. Если устройство не заменяет одно из стандартных устройств MS-DOS (т.е. если оно не названо одним из резервных слов, J'а­ ким, как PRN, AUX и т.д.), то вы можете открыть устройство од­ ной из функций для открытия файла. Вы можете использовать как метод доступа с помощью управляющего блока файла, так и метод дескриптора файла, хотя последний предпочтительнее. Чтобы быть уверенным, что вы по ошибке не откроете дисковый файл, помес­ тите номер файла в ВХ, О - в AL, после чего выполните функцию 44Н прерывания 21Н. Это функция IOCTL, и если бит 7 значения, возвращаемого в DL, установлен, то драйвер устройства загружен. IOCTL требует, чтобы в байте атрибутов драйвера была соот­ ветствующая установка битов и чтобы по крайней мере основы процедуры обработки IOCTL имелись в процедуре обработчика прерывания драйвера. Функция IOCTL имеет 8 подфункций, п'ро­ нумерованных от О до 7, при этом соответствующий кодовый номер помещается в AL при вызове функции: О возвратить информацию об устройстве в DX 1 установить информацию об устройстве, используя DL (DH =0) 2 считать СХ байт от драйвера устройства через управляющ~•й канал и помес­ тить их, начиная с DS:DX 3 записать СХ байт в драйвер устройства через управляющий канал, взяв их начиная с DS:DX 4 то же, что и 2, но использовать номер накопителя в BL, где О = накошпель по умолчанию,_ 1 = А и т.д. 5 то же, что и 3, но использовать номер накопителя как в 4 б получить статус ввода 7 получить статус вывода
Ввод/вывод 473 В ответ возвращается различная информация, в зависимости от - того, какая функция вызвана. Для подфункций О и l значение битов регистра DX следующее (при условии, что бит 7 = l, что означает, что доступ получен к устройству, а не к файлу): ~r~-:_ ~--. · '),L. о устройство кQнсольного ввода 1 устройство консольного вывода 2 нулевое устройство 3 устройство часы 4 резерв 5 1 = нет проверки на' Ct}J-Z, О есть nроверюа на Ctrl б 1 = не конец файла, О = конец файла 7 1 = устройство, О = дисковый файл 8-13 резерв 14 1 = если можно использовать nодфункц11и 2 _и 3, О нельзя 15 резерв . Подфункции 2-5 i:юзволяют программе и устройству обмени­ ваться произвольными управляющими строками. Это дает возмож­ ность передавать у,правляющие сообщения отдельно от основного потока данных, что существенно упрощает дело. При возврате АХ будет содержать число переданных байтов. Подфункции 6-7 позво­ ляют программе проверить, готово ли устройство для ввода или вывода. Для -устройств в AL возвращается FF, если· оно готово, и О, если нет. Если же эти подфункции применить к открытому файлу (бит 7 • О), в AL возвраПUtется FF до тех пор, пока пе будет достигнут конец файла. Отметим, что в Бейсике 3.0 добавлены операторы IOCTL и IOCTL$. Они позволяют бейсиковской программе ~оответственно посылать и• принимать управляющие строки от драйвера устройства, которое было предварительно открыто оператором OPEN. Выходная строка должна быть заключена в кавычки, как в IOCTL #3,"... ". Подобным образом А$ ·;,. IOCTL$(3) принимает информацию о статусе через IOCTL. ,. 7.1 .S. Обнаружение и анаnнз ошибок устройства Устройства могут ошибаться по одной из трех причин. •Уст­ ройство может быть физически повреждено •или находиться не в . том состоянии. Црограммное обеспечение, управляющее устройс­ твом, может работать неправильно, и, наконец, програм·ма может послать· устройству недопустимый запрос (например, попытка писать на накопитель, где находится дискета, защищенная от
474 Глава 7 записи). MS-DOS обнаруживает и анализирует большинство таких ошибок и обеспечивает возможности для восстановления. Высокий уровень Интерпретатор Бейсика обнаруживает мноrие ощибки, вклю­ чая ошибки драйверов устройств. При их обнаружении возвраща­ ется код ошибки, и если не предусмотрена программа восстанов­ ления при ошибках, программа останавливается. Однако можно установить обработку ошибок, с тем чтобы, коrда происходит кри­ тическая ошибка, Бейсик автоматически переходил на процедуру восстановления при сбоях, созданную вами. Процедура может про­ анализировать код и определить, в какой строке программы про­ изошла ошибка. После того как это сделано, программа может принять меры по устранению ошибки либо с помощью пользова­ теля, либо выполняя другую часть программы. По завершении процедуры программа может продолжить выполнение с любого, нужного вам места (лишь с некоторыми ограничениями). Код для тщательного анализа ошибочных ситvаций может сvщественно увеличить размер программы. Отмети~. что компилятор Бейсика фирмы IBM, даже для минимальной проверки на ошибки, потре­ бует дополнительно не менее 4 байт на каждую строку программы. Чтобы разрешить обработку ошибок в Бейсике, поместите в начале программы строку ON ERROR GOSUB n, где n - это номер строки программы, в которой начинается процедура обработки ошибок. При возникновении критической ошибки управление будет передано на эту строку. В начале процедуры поместите ряд строк вида IF ERR = n THEN номерстроки, где n - номер ошибки, взятый из приложения к руководству по Бейсику, содер­ жащему сообщения об ошибках. Номера строк в этих операторах соответствуют началу кода, обрабатывающего данную конкретную ошибку. Эти части могут быть в свою очередь разбиты на куски рядом операторов IF ERL = n THEN номерстроки. ERL возвра­ щает номер строки, в которой произошла ошибка, позволяя проце­ дуре восстановления точно определить ошибочное место. После того как процедура восстановления завершила свою работу, надо использовать оператор RESUME для возврата управ­ ления в ту строку, где произошла ошибка. За этим оператором может ·следовать номер, в этом случае управление будет передано на строку с указанным номером. Однако имейте в виду, что нельзя использовать RESUME для перехода в точку программы, которая находится за пределами 'Процедуры, в которой произошла ошибка. Если восстановление после ошибки невозможно, но необходимо, чтобы программа продолжила свою работу, напишите RESUME NEXT, и управление будет передано на строку, следующую за
Ввод/вывод 475 той, в которой произошла ощибка. Вот общая структура про­ цедуры восстановления в Бейсике: 100 ON ERROR GOSUB 5000 'разрешаем обработку ошибок 5000 IF ERR 61 THEN 5100 'диск полон 501~ IF ERR 71 THEN 5200 •диск не готов 5100 IF ERL = 2080 THEN 5120 'где произошла ошибка? 5110 ВЕЕР: PRINТ "Disk in drive В: is full": RESUME 5120 ВЕЕР: PRINТ "Disk in drive А: is full": RESUME 5200 ВЕЕР: PRINТ "А disk drive 'is not ready" 'сообщение об ошибке записи 5210 PRINТ "Strike any key when corrected" 5220 IF INКEY$ = "" THEN 5220 ·ожидаем нажатия кл{lвиши 5230 RESUME ERL - 10 'пьпаемс11 повторить операцию В Бейсике 3.0 введены инструкции ERDEV и ERDEV$. Обе они позволяют получить переменные только для чтения от преры- . вания 24Н, обрабатывающего критические ошибки. Z% = ERDEV возвращает в Z% .слово статуса, в котором.старший байт содержит 13-15 биты атрибута заrо~овка устройства, а младший байт - код ошибки прерывания 24Н. Z$ = ERDEV$ помещает в Z$ 8-байто­ вое имя устройства для символьных устройств и 2-байтовый указа­ тель накопителя для блочных устройств. Низкий уровень Иногда драйверы устройств содержат такие серьезные ошибки, что программа просто не может выполняться пока они не будут исправлены. Когда такие ошибки происходят, то система вызывает обработчик критических ошибок. Он может вступать в действие как для стандартных устройств, так и для установленных драй­ веров. Пользова:гель наиболее часто сталкивается с ним, когда пытается произвести дисковую операцию с дисководом, у которого открыта дверца. В этом случае появляется сообщение: "Not ready error reading drive А - AЬort, Retry, lgnore?" ОбработчJt:к критических ошибок может быть переписан с целью улучшения обработки устройств, для которых вы создали устанаВJμ1ваемые дwйверы. Вектор прерывания 24 Н укащ:,1вает на стандаJ)ТНую процедуру MS-DOS, но вы можете перенаправить вектор на свою процедуру. При вызове этой процедуры старший бит АН содержит О, если •ошибка произошла на блочном уст-
476 Глава 7 ройстве, и 1, если на символьном. BP:SI указывают на заголовок драйвера виновного устройства, который может дать дополнитель­ ную информацию. Восемь байтов заголовка, начиная со смещения АН, содержат имя устройства, а обработчик критических ошибок помещает код ошибки длиной в слово в DI. Вот кодовые номера (они не представляют битовых позиций): Ко.а Проблема О попытка писать на диск, защищенный от записи неизвестное устройство 2 накопитель не rотов 3 неизвестная команда 4 ошибка обмена данными 5 неверная длина запроса 6 ошибка поиска 7 неизвестный тип носителя 8 сектор не найден 9 нет бумаги в принтере А ошибка при записи В о~ибка при чтении С общая ошибка В случае дисковой ошибки AL содержит ном6J) накопителя, на котором произошла ошибка (О " А, 1 == В и т.д.), а биты 2-0 АН индицируют тип ошибки. Бит О устанавливается, если ошибка произошла во время операции записи, и сбрасывается, если при чтении. Биты 2-1 содержат информацию о том, в каком месте диска произошла ошибка, давая 00 •для начальных секторов DOS, 01 для FAT, 10 для каталога и 11 для всего остального диска. Имеется три способа, с помощью которых программа может восстановиться после критической ошибки. 1. •Можно попросить пользователя устранить причину ошибки (например, закрыть дверцу накопителя), после чего сис­ тема предоставит устройству возможность повторить опе- рацию. , 2. Управление может быть возвращено инструкции, следующей за INT 21Н, которая сделада попытку обратиться к драйверу. 3. Программа· может завершиться и вернуть управление с~с­ теме. Ваша процедура обработки ошибок может восстанов~ть ситу­ ацию, выдав инструкцию IRET, после того как она поместила О в AL, чтобы игнорировать ошибку, 1, чтобы повторить операцию, и 2, чтобы завершить программу. Если вы хотите, чтобы ваша про-
Ввод/вывод 477 цедура провела восстано~Sлеi-lие сама, то она должна восстанов11ть регистры выполняемой программы из стека, а затем удалить со стека все, кроме последних трех слов. После этоrо инструкция IRET возвратит управление программе, хотя сама системц оста­ нется в нестабильном состоянии до тех пор, пока она не J&lie:MQYl вызов функции с номером, большим чем 12. Вот конфигу.рация стека (начиная сверху вниз) при вызове обработчика критических ошибок: Адрес возврата обработчика ошибок: IP, CS, флаr11 Пользqвательские ре~1стры задачи, АХ, ВХ, СХ, DX, SI, DI, из которой был вызван драйвер: ВР, DS, ES, IP, CS, флаг11 MS-DOS обрабатывает также многие некритические ошибки. Сюд_а включаются коды ошttбок, которые могут возвращаться в регистрах, при обращении к функции DOS. Эtи коды обсуждаются в данной книге в тех местах, в которых описываются соответству­ ющие функции. Однако имейте в виду, что, начиная с версии 3.0, MS-DOS возвращает расширенные коды ошибок для функций, использующих FCB или дескрипторы файлов. Коrда при выпол­ нении одной из этих функций устанавливается флаr переноса, в АХ врзвращается обычный код ошибки. Дополнител1,ный расши­ ренный код доступен через прерывание 59Н, если в ВХ поместит~, О. Эта функция сообщает также о критических ошибках, и она может быть использована в обработчике критических о·шибок, вызываемом через прерывание 24Н. • Функция помещает в АХ код ошибки, взs1тый из обычного списка знакомых кодов ошибок (например, "недостаточно памяти") или один из новых кодов (например, "ограничение дос­ тупа" для мноrопользовательской системы). вн возвращает код класса ошибки, указывая, какоrо типа произошла ошибка. Напри­ мер, код 1 предупреждает; что исчерпаны ресурсы, т.е. что память, файловые буфера или что-10 еще израсходовано. Другие классы могут указывать на программные ошибки, проблемы с носителями, форматированием и т.д. BL содержит код, предполагающий дейс­ твие для восстановления, такие, как "повторит~,", "прекратит~," или "запросить у пользователя". Наконец, СН возвращает число, определяющее место, где возникли проблемы: на блочном уст­ ройстве, на символьном, в памяти? Данные для этих кодов ошибок весьма обширны. Полную информацию о них см. в Техниче~ком руководстве по MS-DOS 3.0 . Поскольку предполагается, что MS-DOS 3.0 нс будет функциони­ ровать на машинах, более ранних, чем АТ, то испол1,зованис этих кодов ограничи_вает совместимость ваших программ. Тем нс менее
478 Глава 7 набор процедур, предназначенный только для MS-DOS 3.0, может дополняться сверх обычных процедур обработки ошибок. В [ l. l .3] показано, как программа может определить версию MS- DOS, в которой она работает. Наконец, имейте в виду, что процесс может передавать код завершения вызвавшему его процессу. Термин процесс относится к взаимодействующим программам. Например, когда одна программа загружает и запускает другую с помощью функции ЕХЕС, то запускаемая программа называется потомком, а запускающая - родителем. 'Родителю может потребоваться информация о том, как завершился потомок. Чтобы использовать эту, возможность, помес­ тите желаемый код завершения в AL и выполните функцию 4СН прерывания 21Н для завершения программы. Когда управление будет возвращено родителю; то он выполнит функцию 4DH преры­ вания 21Н (без входных регистров), и в AL будет получен код завершения, который может затем быть проанализирован. Кроме того, АН будет содержать информацию, о том, как завершился потомок: О - для нормального завершения, 1 - по Ctrl-Break, 2 - по критической ошибке устройства и 3 - с помощью функции ЗlН, оставляющей программу резидентной. Если программа завершилась с помощью этой функции (а нс 20Н, см. обсуждение в [1.3.4 ]), то MS-DOS получает код выхода и он может быть включен в обработку командным файлом с помо­ щью подкоманды IF. Эта подкоманда п_озволяст условное иск"1ЮLIС­ ние других команд из командного файла. Код выхода рассматрива­ ется как номер ERRORLEVEL и условные операции выполняются в зависимости от того, больше он определенного числа или нет. С помощью этой возможности командные файлы могут прекращать обработку и выводить сообщение о возникновении ошибки в одной из запущенных программ. Более подробная информация приведена в. разделе "Команды пакетной обработки" руководства по опера­ ционной системе. 7.3. Использование специальных устройств ввода/вывода Имеется огромное количество устройств вво­ да/вывода, которые могут быть присоединены к IBM РС, включая мышь, дЖQЙСтик, графопостроители и т.д. В данном разделе обсуждаются только те устройства, которые специал1,но поддержи­ ваются оборудованием IBM РС, например кассетные магнитофоны,
Ввод/вывод 479 световое neJIO и другие устJЮйства, которые могут быть присоеди­ нены через ИГJЮВОЙ порт. Адреса портов, относящиеся к другим устJЮйствам, обсуждаются в соответствующих разделах этой книги.. Распределение адресов портов в основном одицаково для всех типов IBM РС: Адре~; порта 00-0F 20-2F 40-4F 60-бF 70-7F 80-83 AO-BF СО-С7 FO-FF IFO-IF8 200-20F 278-27F 2F8-2FF 320-32F 378-37F 380-ЗВF 300-ЗDF ЗFО-ЗF7 ЗF8-ЗFF Функция микросхема DMA 8237 (не для PCjr) микросхема прерываний 8259 (АТ контроллер # l: 20-ЗF) микросхема таймера 8253/8~4 микросхема PPI 8255 (АТ использует только адреса клавиатуры) часы реального времени (тw~ько для АТ) регистры страниц DMA (не для PCjr) микросхема прерываний #2 (только для АТ) микросхема звука SN76496 (тош,ко для PCjr) PCjr - контроллер НГМД, АТ - уnра1u1ение математи11еским сопроцессором фиксированный диск АТ игровой адаптер АТ коммуникационный порт #2 коммуникационный порт СОМ2 <СОМ I для PCjrJ фиксированный диск ХТ адаптер nаралле11ыюrо 11ршпсра дли l'C, ХТ, ЛТ монохромный/параллельный адаптеры (не для PCjr) • цветной rрафи11еский адаптер контроллер НГМД коммун•1кац•юнный адаптер СОМ 1 (модем PCjr) 7.3.1. Чтение/запись с кассетноrо маrнитофона Только очень немногие IBM РС и PCjr имеют кассетный маг.:. нитофон, а ХТ и АТ не. поддерживают его вообще. Помимо того, что он очень ненадежен, обмен с кассетным магнитофоном возмо­ жен только последовательный, но не с прямым доступом. Тем не менее могут быть цричины для ПJЮграммиJЮвания кассетного маг­ нитофона на PCjr. Имейте в виду, что кассетные операции исполь­ зуют канал 2 микJЮСхемы таймера 8253 <[2.1 .1 )) , поэтому не пытайтесь одновременно использовать этот канал для друrих це_п:ей. Отметим также, что при операции чтения с кассеты запре-
480 Глава 7 щено прерывание времени суток, поэтому счетчик времени суток ВIOS будет давать неверное значение. Высокий уровень ru. ., , •· Хоrя кассетные файлы обрабатываются совсем по-другому, чем дисковые, однако команды доступа к ним совершенно аналогичны. На кассету могут записываться только программные файлы и пос­ ледовательные файлы данных. Последние могут включать файлы изображения памяти. Отметим, что данные не могут добавлятрея к последовательным файлам. При создании именам файлов даются следующие однобайтовые расширения: .В программа на Бейсике .Р защищенная программа на Бейсике .А программа на Бейсике в формате ASCII .М файл изображения памяти .D последовательный файл данных Для сохранения файла на кассете напишите SA VE ''САSl:имяфайла", для, загруз:юи программы LOAD "САSl:имяфайла". В последнем случае лента прогоняется до тех пор, пока нужный файл не будет найден, при этом имя каждого встреченного файла будет выводиться на экран (кассеты не используют каталоги). Если запросить несуществующий файл, то будет выведен полный список файлов на кассете. Средний уровень BIOS работает с кассетной лентой порциями, состоящими из 256-байтовых блоков. Набору блоков предшествует "лидер_", кото­ рый состоит из 256 байт ASCII 1. Лидер завершается нулевым битом синхронизации. Затем следует байт синхронизации со зна­ чением 16Н, а затем - 256 байт данных. После этого идут 2 байта контроля ошибок, а далее - новый. блок данных, сопровождаю­ щийся парой байтов проверки ошибок и т.д. Вся последователь­ ность завершается четырехбайтовым "хвостом", содержащим коды ASCII 1. Для чтения данных с кассеты надо использовать функцию 2 прерывания -15Н. Нет необходимости открывать файл, как это делается при дисковых операциях. ES:BX указывают на буфер в памяти, куда· будут посылаться данные, а СХ - число байтов, кото­ рые надо считать. При возврате DX сообщит, сколько байтов про­ читано на самом деле, а ES:BX будут указывать на последний счи-
Ввод/вывод 481 танный байт плюс 1. Флаг переноса будет равен О, если чтение прошло успешно, в противном случае .АН будет содержать 1, если проблема с контролем ошибки, 2 - при ошибке чтения данных и 3 - при отсутствии данных на ленте. Функция 3 прерывания 15Н записывает .данные на кассету. ES:BX указывают на первый байт данных, а СХ содержит число байтов, которое надо записать. При возврате ES:BX указывают на байт, следующий за .последним записанным. Мотор управляется функциями О (включение) и 1 (выключение) прерывания 15Н. Для этих функций нет выходных регистров. 7.3 ..1. Чтение позиции световоrо пера Хотя компьютеров, оснащенных световым пером довольно мало, тем не менее это одно из немногих вспомогательных •уст­ ройств, которое поддерживается как оборудованием, так и опера.:. ционной системой. Световое перо работает с помощью небольшого оптическоrо детектора, находящегося на ero кончике. По ходу ска­ нирования экрана электронным лучом инициируется импульс оптического детектора, когда пучок достигает точки экрана, над которой находится перо. Время возникновения этоrо импульса относительно сигналов горизонтальной и вертикальной сцнхрони­ зации позволяет определить позицию светового пера. Высокий уровень Бейсик может определять позицию светового пера двумя спосо­ бами. При первом программа непрерывно определяет статус пера. При втором управление временно (до окончания работы пера) передается процедуре, обеспечиваемой вашей ирограммой. Для непрерывного контроля за пером надо использовать оператор PEN как функцию в форме Х = PEN (n), где n - кодовый номер, опре­ деляющий, какую инфЬрмацию о пере и его позиции вы хотите получить. Возможные значения n таковы: О возвращает -1, если перо было выключено со времени последнего запроса, О-еслинет возвращает последнюю координату х (0-319 или 0-639), в которой перо было вкл'ючено (оно могло быть впоследствии передвинуто, если оста• валось включенным) 2 возвращает последнюю координату у (0-199), в которой перо было включено. / 3 возвращает -1, если перо включено, и О - если нет 16 Р. Джордейн
4 возвращает текущую х координату (0-319 ~•;ш 0-639> 11срн 5 возвращает текущую у координату (0-199) 11ера 6 возвращает позицию - 1юмер строки ( 1-24>. 11 которой 11е1ю 61,1;ю 1юс;1сд­ ний раз активизировано 7 возвращает позицию - номер столбца ( 1-40 ш~и 1-1!0), 11 которой 11е1ю бмло пОСJJедний раз акп1виз~1ровано 8 возвращает текущую позицию - 1юмер строки О -24) 9 возвращает текущую_ позицию - номер столбца О - 40 или 1-80) В данном примере определяется, включено ли перо, и если это -так, то устанавливается ero текущее положение: 100 IF NOT PEN(3) THEN 130 110 Х = PEN(4) 120 У = PEN(S) 130 ... проuер11ем, uклю1 1с1ю ди 11еро ·подучаем коорди~шту то1 1ки 1ю оси х ·,юлучасм координату то•1ки 1ю оси у продолжаем прш·р11мму Более гибкие возможности использонаниs1 светового пера пре­ доставляются оператором ON PEN GOSUB. Этот оператор указы­ вает номер строки, в которой начин,tется процедура, активизиру­ емая при включении пера.· Бейсик достигает этого проверкой сос­ тояния пера после выполнения каждой его инструкции. Процедура может определить позицию пера и предпринять требуемые дейст­ вия. Когда процедура заканtrивается, программа продолжается с того места, где она была при включении пера. , ON PEN GOSUB не работает до тех пор, пока она не активи­ зирована оператором PEN ON. PEN OFF отменяет се работу. Смысл этоrо в том, что постоянная проверка статуса пера замед­ ляет работу программы, поэтому ее надо осуществлять, только когда это необходимо. Если программа начинает выполнять крити­ ческую часть кода, когда нельзя использовать процедуру ON PEN GOSUB, напишите PEN STOP. В этом случае будет продолжаться проверка статуса пера, и если перо будет включено, то этот факт будет запомнен. Однако пока не будет встречен оператор PEN ON, управление не будет передаваться процедуре ON PEN GOSUB. В следующем примере остановка программы происходит в момент .нажатия кнопки на световом пере. Точка в позиции свето­ вою пера включается процедурой, обрабатывающей включение световою пера. 100 ON PEN GOSUB 5000 ·устанаw1иuаем процедуру JtJIЯ с11ето11011, 110 PEN ON пера ~• uклю•шем рс~им ею отс;1ежищ111ия
Ввод/вывод 5000 '"процедура обработки светового пера 5010 Х = PEN(4) ·получаем коорди11ату Х 5020 У = PEN(5) ·получаем координату У 5030 PSET(X,Y) 5040 REТURN С~дний уровень ·включить эту точку 483 Функция 4 прерывания 1ОН BIOS сообщает текущую позицию световоrо пера. У нее нет входных регистров. При возврате АХ содержит О, еслй перо не включено, и 1, если получены новые значения для ero позиции. Возвращаются два набора координат: позиции точки и позиции строки и столбца. Позиции символа содержатся в DX, причем DH содержит строку (0-24), а DL - стол­ бец (0-79). Позиция точки содержится в СН и ВХ, где СН содер­ жит вертикальную координату (0-199), а ВХ - rоризонтальную (0-319 или 0-639 в зависимости от режима терминала). ;---читаем и зап~минаем положение светового пера MOV АН,4 ;номер функц~1и INT I0H ;прерывание BIOS СМР АН,1 ;новая позиция? JE · NO_READING ;если нет-, то уходим. MOV COL,BX ;сохраняем rоризо11та.,1ы1ук, коордищ~ту MOV CL.CH ;помещаем вертикалы1ую коорди11ату MOV СН,О ;в СХ MOV ROW,CX ;сохраняем' вертикальну10 коордш1а:ту Низкий уровень По своей сути световое I:Jepo является расширением видеосисте­ мы и как таковое использует микросхему контроллера CRT 6845. Позиция световою пера дается одним двухбайтовым значением, хранящимся в регистрах l0H (старший байт) и l lH (младший байт) микросхемы. В (4.1.1] объясняется, как читать регистры микJ)QСХемы. Посмотрите пример. Порт с адресом 3QCH устанав­ ливает задвижку световоrо пера, а с номером ЗDВН сбрасывает ее. 16* ✓ ;---проверка светового пера и чтение его позиции MOV DХ,ЗDАН ;указываем на регистр cтnrycn 1N AL,DX ;получаем информацию ТЕSТ AL,4 ;проверяем выключатель JNZ NOT_SET ;на выход
484 TEST AL,2 JZ NOT_SET SUB DX,7 моv AL,l0H оuт DX,AL lNC DX IN AL,DX XCNGAН,AL DEC DX MOV AL,llH OUT DX,AL INC DX IN AL,DX ;проверяем три~rер ;на выход • ;указываем на регистр адреса 6845 ;запрос на старший байт позиции пера ;посылаем запрос ;указываем на регистр данных 6845 ;получаем значение ;запоминаем его в АН ;возвращаемся к адресному регистру ;теперь хотим получить младший байт ;посылаем запрос _ ;назад к регистру данных ;теперь это значение в АХ Глава 7 7.3.3. Попученне aнanoroвoro ввода через нrровой порт Игровой порт может поддерживать 2 джойстика или 4 "ручки"* . Для джойстика он сообщает две координаты и статус двух кнопок; для ручки он сообщает одну координату и статус одной кнопки. Несколько вспомогательных устройств, таких, как графическое табло, также может быть подключено к игровому порту; их работа может осуществляться параллельно с работой джойстика. Данный раздел посвящен чтению координат, а в следу­ ющем обсуждается, как получить статус кнопок. Высокий уровень Функция STICK возвращает позиции по осям, указываемые следующими кодовыми номерами: О ось Х джойстика А 1 ось У джойстика А 2 ось Х джойстика В 3 ось У джойстика В Вам нужно написать, например, Х = STICK(O) и в Х будет содержаться значение координаты Х для джойстика А. Но эта функция имеет одну особенность, о которой вам необходимо знать. *Здесь и далее термином "ручка" мы будем называть специальную ручку для упрамени11 игрой (англ. paddle). В Советском Союзе это устройство мало распространено, и мне неизвестно общеупотребительное его название. - Примеч. пер.
Ввод/вывод 485 Только при использовании кода О действительно читаются коорди­ наты джойстика, при этом читаются все 4 значения. Кодовые номера 1-3 просто сообщают показания, прочитанные кодом О. Чтобы получить щх:.ледние 3 координаты, вам необходимо перед этим использовать функцию Х • -STICK<O>, даже если вам не нужно знать значение, возвращаемое кодом О. • Джойстики различаются по своим физическим характерис­ тикам, поэтому необходимо настраивать их, чтобы их предельные положения совпадали с границами экрана. В следующем примере показано, как это делается. В примере непрерывно рисуется точка в позиции, указываемой джойстиком, действие, которое требуется, чтобы диапазон значений, принимаемых игровым портом, преобра­ зовывался в диапазо1_1 пQЗиций экрана. 100 "'получаем nредмьные показания джойстика 110 SТRIG ON • ·разрешаем кнопки 12q V" SТRIG(0) чистим старые показания 1.30 PRINТ "BrleПy push button I when stick is farthest to left" 140 XLEFf = SТICK(0), ·получаем самое левое значение . 150 IF SТRIG(0) = О THEN 140 ждем нажатия кнопки 160 SТRIG,OFF: FOR N = 1 ТО 1000: NEXT: STRlG ON 170 PRINT "BrleПy push button I when stick is farthest to right". 180 XRIGHT = SТICK(0) получаем самое npa110e зна•1ение 190 IF SТRIG(0) = О THEN 180 '-k<дем нажатия кнопки 200 SТRIG OFF: FOR N = 1 ТО 1000: NEXT: SТRIG ON 210 PRINТ'"BrleПy push button i when stick is farthest to top" 220 V " SТICK(0): УТОР = SТICKO) _·самое верхнее ~начение 230 IF SТRIG(0) • О THEN 220 ·ждем нажатия кнопки 240 SТRIG OFF: FOR N = 1 ТО 1000: NEXT: SТRIG ON 250 PRINТ "BrleПy push button I when stick farthest to lюttom" 260 V = SТIC_K(0): УВОТ = STICKO) ·сам~ нижнее зна•1ение 270 IF SТRIG(0) = О THEN 260 280 SТRIG OFF ждем нажатия кнопки ·закончили 290 '"получаем множите.пи для установки размера экра11а 300 XRIGHT = XRIGHT - XLEFf ··1'0ризонта.,1ьный размер 310 XMULTIPUER " 320/XRIGHT ·вычисляем число точек на деле11ие 320 УВОТ = УВОТ - УТОР ·вертикальныf1 размер 330 YМULТIPUER = 200/8/УВОТ чисJ10 точек на деление 340 '"теперь вычисляем координаты в реж ►1ме умере,шоrо р.1зрешения 350 Х = SТIСК(О) ·получаем горизонтальную позицию 360 У = SТIСКШ ·получаем вертикальную позицию
486 Глава 7 370 Х = (Х - XLEFГ)*XМULТIPRIER 'приводим к экрану 380 У = (У - YГOP)*YМULТIPRIER 390 PSET(X,Y) 'выводим точку в нужной позиции 400 GOTO 350 'повторяем Средний· уровень Только АТ предоставляет поддержку джойстика на уровне опе­ рационной системы. Это функция 84Н прерывания 15Н, которая возвращает координаты, причем: АХ вх сх DX координата Х джойстика А координата У джойстика А координата Х джойстика В координата У джойстика В При входе поместите в DX 1. Когда в DX содержится О, функ­ ция возвращает состояние кнопок джойстика [7 .3 .4 ]. Когда у машины нет игрового порта, при возврате устанавливается флаг переноса. • Низк;.й уровень Информация о координатах обоих джойстиков или всех четы­ рех ручек хранится в одном байте, доступ к которому осуществля­ ется через порт 201Н. Вот значение его битов: Бит Джойстик Ручка 3 координата У джойстика В координата ручки D 2 координата Х джойстика В координата ручки С 1 координата У джойстика А координата ручки В о координата Х джойстика А координата ручки А Координата может 'описываться одним битом за счет измерения временных интервалов. Пошлите в порт байт с произвольным зна­ чением. Это приведет к тому, что младшие 4 бита обнулятся. Затем постоянно считывайте значение из порта, замеряя интервал времени, через который интересующий вас бит станет равным 1. Этот интервал пропорционален позиции джойстика по интере­ сующей вас оси. Максимальные интервалы соответствуют нижней позиции по оси У и правой позиции по оси Х. Независимо от
Ввод/ вьtвод 487 позиции, биты меняются от О к 1 очень быстро, по сравнению с . механической скоростью перемещения джойстика или ручки. Поэтому программа может с большой точностью получить сначала позицию координаты У, а затем позицию координаты Х. Нет необ­ ходимости тестировать каждую координату .отдельно. В пред­ лагаемом- ниже примере определяется координата Х джойстика А. ;---получаем координату Х джойстика А MOV DX,201H ;адрес игрового порта OUT DX,AL ;посылаем в порт произвольное значение MOV АН,1 ;проверяем бит 1 MOV Sl,0 ;инициализируем счетчик • NEXT:IN AL,DX ;читаем значение из порта TEST АL,АН ;проверяем бит 1 JE FINISHED ;выход, когда бит установлен INC SI ;иначе, увеличиваем счетчик LOOP NEXT FINISHED: ;на начало цикла ;теперь в SI находится Х-координата ру•1кн А 7.3.4. Получение цнфровоrо ввода нз нrровоrо порта Игровой порт поддерживает два джойстика или четыре ручки, а также ряд графических устройств. При этом одновременно может определяться статус не более четырех кнопок устройств: Проверка состояния кнопок ·оказывается сложным делом, поскольку программа не имеет возможности постоянно проверять их, а кнопка может быть нажата и отпущена, пока программа занята другими делами. Для решения этой проблемы создают специальную процедуру. Статус кнопок автоматически читается несколько раз в секунду без специального запроса н.t это программы; когда оказывается, что кнопка нажата, то управление передается процедуре, которая определяет, какая кнопка нажата и предпринимает нужные действия. Высокий уровень Бейсик использует оператор STRIG для чтения статуса кнопок. Оператор STRIG достаточно "хитрый", чтобы обнаружить нажатие кнопки, даже если программа в данный момент не проверяет ста­ тус кнопки, т.е. программа может запросить: "Было ли нажатие кнопки, со време11и моеrо • последнего запJ><?Са?". Это свойство очень полезно для видеоигр, поскольку программа может зани-
488 Глава 7 маться манипуляциями с экраном, нс заботясь о постоянной про­ верке статуса кнопок. Однако это существенно замедляет скорость выполнения программы, поскольку проверка статуса кнопок осу­ ществляется после выполнения каждого оператора. По этой при­ чине STRIG работает, только когда он преднамеренно включен, а он может включаться и выключаться по ходу программы. STRIG работает двумя способами. Во-первых, он может рабо­ тать как функция, которая непосредственно читает текущие значе­ ния кнопок, в форме Х = STRIG(n). Здесь n - кодовый номер: О кнопк:~ AI нажата со времени последнеl'О вызова 1 кнопка А! в данный момент оп1уще11а 2 кнопка 81 нажата со времени послед11е1·0 пызош1 3 кнопка 81 в данный момент отпущещ1 4 кнопка А2 нажата со 11ременf1 последне1·0 11ызощ1 5 кнопка А2 в данный момент отпущена 6 кнопка 82 нажата со времени последнего ш,1зо11а 7 кнопка 82 в данный момент отпущена Во всех случаях функция возвращает -1, если описание приме­ нимо, ,и О, если нет. Второй способ применения STRIG это форма, в которой он автоматически передает управление процедуре при нажатии кнопки. Надо написать ON STRIG(n) GOSUB номерстроки. Номер строки дает начальный номер строки процедуры. Число n отно­ ситсяккнопке,гдеО=Al,2 =Bl,4 =А2и6=В2.Каждая кнопка может обрабатываться своей процедурой или может быть одна процедура для всех кнопок. Для активизации функции STRIG включите в программу опе­ ратор STRIG(n) ON. В качестве значения n используются четыре ' перечисленных кода. Чтобы отменить ero (ускоряя работу прог­ раммы), напишите STRIG(n) OFF.· Имеется также третья возмож­ ность. STRING(n) STOP приводит к тому, что нажатие кнопки запоминается, но никаких действий не предпринимается до оче­ редного оператора STRING(n) ON. Это свойство предохраняет от нежелательных перерывов из-за оператора ON STRING GOSUB. Однако при выполнении условия STRIG(n) STOP программа замедляется. В следующем примере показано действие ON STRJG GOSUB. В примере параграфа [7.3 .3 J содержатся строки, демонстрирующие форму Х = STRIG. 100 ON STRIG(0) GOSUB sооо·переход на 5000 11pf1 нажатии KH0ЛKfl AI 200 SТRIG(O) ON ·включаем проверку нажатия кнопки
Ввод/вывод 300 SТRIG(O) STOP 400 SТRIG(O) ON 500 SТRIG(O) OFF 489 •отмен11ем уход .на процедуру 'возобновляем уход на процедуру. 'отменяем проверку. нажатия кнопки 5000 "'здесь находится процедура обработки нажатия кнопки Al 5500 RETURN Средний уровень ·возврат к тому месту, откуда попали сюда / / Только АТ предоставляет поддержку джойстика на уравне опе­ рационной сис:rемы. Функция 84Н прерывания lSH возвращает установку кнопок в битах 4- 7 регистра AL, как показано ниже. При входе DX должен содержать О; когда DX содержит 1, то тогда возвращаются координаты джойстика (см. [7 .3 .3 ]) . Если машина не имеет игровоrо порта, то при возврате устанавливается бит переноса~. ;---проверяем кнопку #2 джойстика В (бит 7>, MOV АН.84Н ;номер функции MOV DX,0 INT 15Н ;~прос состояния кнопок ;вызов функции JC. NO_JOYmcк TEST AL,100000008 ;если нет джойстика, то на выход ;проверяем бит 7 JNZ ВUТТОN_DOWN • ;переход, если кнопка нажата Низкий уровень Биты: 7-4 порта с.адресом 20IH содержат статус кнопок, свя­ занных с игровым портом. Значение битов меняется в зависимости от тоrо, присоединены\ли джойстики или. ручки: Бит Джойстик Ручка 7 Кнопка #2 джойстика В Кнопка ручки D 6 Кнопка #1 джойстика В Кнопка ручки С 5 Кнопка #2 джойстика А Кнопка ручки В 4 Кнопка' #1 джойстика А Кнопка ручки А
490 Глава 7 , Программе нужно просто прочитать значение из порта и проверить установку соответствующих битов: MOV DХ,201Н IN AL,DX TEST AL,00108 JNZ ВUТГОN_А2 ;адрес порта ~щю1юm а;щнтсра ;получаем значение нз него ;проверяем бю 1 (кнопка Л2 ш1жата"J ;если да, то на процедуру обработн1 Программа имеет обычно более важные дела, чем постоянная проверка игрового порта, однако настолько же быссмысленной ока­ зывается периодическая проверка порта путем помещения проце­ дуры в разные части программы. Чтобы получить эффект слеже­ ния за нажатием кнопок, аналогичный описанному в Бейсике, вам придется создать дополнение к прерыванию времени суток, как описано в [2.1 .7 ]. Прерывание обычно выполняется 18,2 раза в секунду, и каждый раз вы можете проверять игровой порт и при необходимости предпринимать нужные действия.
При11ожение А. Двоичные и шест­ надцатиричные чис11а и адрееация памяти • Основной единицей хранения: данных в ком­ пыотере является бит. В большинстве микрокомпьютеров восемь битов объединены в байт, при этом каждый бит байта может быть установлен ("включен" ( • 1)) или сброшен или ("выключен"- ( • О)), допуская 256 разных вариантов. Таким образом, в одном байте можно представить 256 различных символов (расширенный набор кодов ASCII) или целое число в диапазоне от О до 255. Хотя мы привыКJiи записывать эти числа в десятичной форме, они могут записываться также в двоичной или шестнадцатиричной форме - их значения при этом не изменяются, а программы могут , с одинаковой легкостью читать эти значения как в той, так и в друrой форме. Вместо тоrо чтобы rоворить, что в одном байте могут храниться числа от О до 255, можно сказать, что могут храниться двоичные числа от 00000000 до 11111111 или шестнадцатиричные числа от 00 до FF. Поскольку можно перепутать разные формы, то двоичные и шестнадцатиричные числа отмечаются специальным образом. В языке ассемблера за двоичными числами следует буква В, а за шестнадцатиричными числами - буква Н, например, 11111111В или FFiI. Бейсик фирмы Microsoft предваряет шестнадцатиричные числа символами &Н, например &FFH; к сожалению', числа в двоичной форме он не распознает. . Двоичные числа Когда содержимое байта представляется в двоичной форме, то требуется 8 цифр. Каждая цифра соответствуе,: одному биту; биты нумеруются от О до 7. Как и в десятичных числах, цифры распо­ лагаются справа налево, от ·младших к старшим разрядам. В отли­ чие от десятичных чисел, в которых каждая последующая цифра в 10 раз больше своей соседки справа, двоичные цифры увеличива­ ются только в два раза. Таким образом, самая правая цифра счи­ тает единицЬI, вторая - двойки, третья - четверки и т.д. до значе­ ния 128 для восьмой цифры б~йта. Это означает, что если рервая цифра- равна 1, то прибавление к ней 1 приводит к тому, что она
492 Приложения станет О, .а 1 будет перенесена во /вторую цифру, как для деся­ тичных чисел 9 + 1 = О и перенос единицы в следующий разряд. Вот как числа первого десятка представляются в' двоичной форме: 00000000 о 00000001 00000010 2 00000011 3 00000100 4 00000101 5 00000110 6 00000111 7 00001000 8 00001001 9 00001010 10 В этой последовательности большинство нулей с.,1ева необяза­ тельно, т.е. ее можно записать и в виде О, 1, 10, 11, 100, 101 и т.д. Нули включены: только для того, чтобы напомнить вам, что байт состоит из восьми цифр, соответствующих битам. Возможно, вам покажется, ч_:rо работать с набором нулей и единиц несколько утомительно, поэтому вы можете облегчить задачу, если будете представлять себе двоичные числа следующим образом: бит значение 7 128 6 64 5 32 4 16 3 8 2 ,4 1 2 о Когда вы встречаете двоичное число 10000001 , то установлены биты 7 и О. Бит 7 соответствует 128, а бит О - 1, поэтому деся­ тичное значение байта равно 129. Если этот байт представляет символ, то он соответствует коду ASCII 129, который представляет букву u с умляутом (в альтернативной кодировке ГОСТа - букву Б). Наоборот, чтобы определить цепочку битов для буквы А, рав­ ной ASCII 65, просмотрите при-веденную выше таблицу на зна­ чения битов, которые она содержит: 64 и 1, что соответствует 0l00000lB. Зачем нужно связываться с двоичными числами? Одна из qри­ чин состоит в том, что компьютер хранит информацию в статус­ ных байтах памяти и статусных регистрах микросхем. Отдельные части этой информации распределены по одному или двум байтам. Это достигается назначением определенных битов определенным данным. Например, помимо других вещей, байт статуса может
493 сообщить, сколько пр1tнтеров и дисковых накопителей присоеди­ нено к вашей машине. Скажем, два старших байта содержат число принтеров, а два •младших - число дисковых накопителей. Байт статуса расположен в определенной ячейке памяти и, как и любой байт, может иметь значения от О до 255. Если значение этоrо байта равно 66 или О 100001 О в двоичной форме, то два старших байта равны 01, а два младших байта - 10. Первая пара говорит о том, что у нас имеется один принтер, а вторая - что 2 дисковых накопителя. Группа битов, рассматриваемая совместно таким обра­ зом, называется полем. Часто вашим программам приходится читать статусные байты или регистры, а иногда изменять уста­ новку битов. Для языка ассемблера эти опера·ции тривиальны, но не для Бейсика. В приложении Б объясняется, как они выпол­ няются в Бейсике. Шестнадца,.-иричные числа В то время как в двоичных числах каждая последующая цифра вдвое больше предыдущей, в шестнадцатиричных числах каждая iщследующая цифра больше в 16 раз. У десятичных чисел перваs~ позиция соответствует единицам, вторая - десяткам, третья - сот­ ням. У двоичных чисс;л первая позиция соответствует единицам, вторая двойкам; третья - четверкам. У шестнадцатиричных чисел первая поз~ция соответствует единицам, вторая - 16, третья - . 256 и т.д. Это означает, что когда в позиции единиц расположена цифра 9, то прибавление единицы н·е приводит к переносу в следу­ ющий разрц, как это было бы в случае десятичных чисел. Но как записать десятичное ЧИСJ!о 1О одной цифрой? Ответ состоит в том, что шестнадцатиричные числа используют первые 6 ·букв латин­ скою алфавита в качес-rве дополнительных цифровых символов: Шестнадцатиричный символ А в с D Е· F Десятичный эквивалент 10 11 12 13 14 15 Перечисление шес-rнадцатиричных чисел продолжается так: 8,9,А,в,с,D,Е,F,10,11 ...19,lA,1ВиТ:д· •
494 Приложения Полезность шестнадцатиричных чисел опирается на тот факт, что одна шестнадцатиричная цифра описывает содержимое ровно l /2 байта. Например, в числе F6 F соответствует старшим четы­ рем битам байта, а 6 - младшим четырем битам (четыре бита, взя­ тые вместе, называются ниблом, или по-русски "огрызком"). Нес­ ложно вычислить двоичный эквива,1ент четверки битов. FH = 111 lB, а 6Н = 0ll0B (напоминаем, что Н и В - суффиксы, помогающие отличить 11 двоичное от 11 десятичного и 11 шест­ надцатиричного). Таким образом, число FбН представляет цепочку битов 11110110. Двухбайтовое число (целое\ может ран­ няться 6FF6H. В этом случае цепочка битов д,1я него имеет вид 0110111111110110. Если число состоит только из трех цифр, то верхняя половина старшего байта равна нулю, например, числу F6FH соответствует цепочка битов 0000 l l l l О 11 О 1111. Шестнадцатиричные числа намного легче читать, чем двоич­ ные. А после небольшой практики оказывается, что работать с ними намного удобнее, чем с десятичными. Адреса памяти и портов Теперь, когда вы научились работать с шестнадцатиричными числами, можно разбираться в системе, которой по,11,зуется про­ цессор при адресации памяти,. Во-первых, важно отмстип,, что имеются два типа адресов: адреса па~1яти и адреса портов. Номера адресов, используемые теми и друrим11, совершенно не связаны; засылка значения в ячейку памяти с адресом 2000 не имеет ничего общего с засылкой значения в порт с адресом 2000. Доступ к портам осуществляется с помощью инструкций INP и OUT в Бейсике и IN и OUT в языке ассемблера. Доступ к адрссам памяти осуществляется в Бейсике инструкциями РЕЕК и РОКЕ, а в языке ассемблера - инструкцией MOV. Имеется 65К доступных адресов портов и 1024К доступных адресов памяти. Поскольку процессор использует 16-битовыс регистры, то он намного быстрее вычисляет адреса памяти, если они не превос­ ходят по длине 16 битов. Однако максимал1,нсс число, которое может содержаться в 16 битах, равно 6553:.5 . Мы будем представ­ лять его как четырехзначное шестнадцатиричное чис.10 FFFFH. Требуется еще 4 добавочных бита, чтобы представит~, такое боль­ шое число, как миллион (FFFFH), которому равен размер адрес­ ного пространства IBM РС (АТ может имеп, доступ к еще бол1,шей памяти, используя виртуальную адресацию, здесь нс рассмат­ риваемую). Процессор решает проблему адресации более чем 64К с помощью 16-битового указателя за счет разбиения памяти на сег­ менты. Сегментом является любая непрерывная область памяти
495 размером 64К; при этом 16-битовый указатель может давать адрес любого, байта внутри него. Процессор хранит положение начала сегмента в мегабайтовом адресном пространстве и рассматривает 16-битовые адреса как смещения относительно этой точки. Но как определить эту точку? Ответ состоит в том, что второе двухбай­ товое значение используется для отметки начала сегмента и это значение умножается на 16 ( = 4 битам) перед вычислением пол­ ного адреса. Таким образом, если это сегментное значение равно 2, то, умножи11 его на 16, получим 32, и адреса будут затем вычисляться как смещение относительно 32-ro байта в г~амяти. Если адрес в сегменте равен 7, то, суммируя 32 и 7, по11учаем, что &ам нужно обратиться к 39-му байту памяти, а не к 7-му. Отно­ сительный адрес (или смещение) этого байта равен 7, а абсолютный - 39 . В Бейсике вы можете установить сеrментны'й адрес с помощью оператора DEF SEG. Если вы напишете оператор DEF SEG = 2, то установите начало сегмента, к которому будете обращаться на 32-й байт, как в предыдущем примере. Затем вы можете исполь­ зовать операторы РЕЕК и РОКЕ для чтения и записи отдел1,ных байтов памяти. Например, РЕЕК(7) прочитает седьмой байт с начала сегмента, т.е. 39-й байт памяти. Во мноmх местах этой книги мы ссылаемся на абсолютные адреса памяти. Это необходимо, поскольку операционная система хранит важную информацию в определенных местах. Абсолютные адреса приводятся в виде 0000:0000, где первые 4 шестнадцати­ ричные цифры указывают адрес сегмента, а вторые - отно­ сительный адрес (смещение). Вспоминая предыдущий пример, мы можем адресовать 39-й байт памяти, записав 0002:0007. Отметим, что тот же самый адрес может быть записан в другом виде, если изменить значение сегментного регистра, например ООО 1:0017. Этот адрес можно представить также в виде одного пятизначного шестнадцатиричного числа. Например, видеобуфер начинается с адреса ВООО:0000, который можно записать как ВООООН. Отметим, что суффикс Н опускается в специальной адресной нотации. И последнее замечание относительно использован11s1 памяти. Когда число занимает два или более байта, то младший байт :поrо числа хранится в ячейке с меньшим адресом. Если целое число А48ВН хранится, начиная с ячейки 1000:0007, то ячейка 0007 содержит 8В, а 0008 - А4. Подобным образом, если вещественное число хранится в памяти как F58CA98DH, то 8D будет храниться в ячейке с самым младшим адресом, а FS - с самьrм старшим.
496 . Приложения Прнложенне Б. Бнтовые операцнн в Бенснке В Бейсике нельзя использовать числа в дво­ ичной форме. Он рассматривает цепочку битов· i 1000000 как 11 миллионов, а не как 192. Однако манипуляции с цепочками битов часто необходимы при программировании, поскольку требуется читать и изменять содержимое статусных байтов ~ статусных­ регистров. В большинстве ·случаев к цепочкам битов· применяются две логические операции. Это операции ИЛИ <OR) и И (AND), и обе Qни доступны в Бейсике. Используемые по отдельности или в ком­ бинации, они позволяют программе читать и устанавливать инди­ видуальные биты байта. Обе операции бинарные, т.е. они приме­ няются к паре значений, давая в качестве результата третье, как обычные арифметические операции: Z = Х OR У. При использо­ вании со значениями байтовой длины эти операции выполняются 8 раз, rio разу для каждого бита. ИЛИ проверяет бит О 'двух байт, и ее.ли этот бит установлен хотя бы _в одном байте, бит О будет уста­ новлен и в результирующем байте. Этот процесс выполняется и для остальных· семи пар битов. Операция И устанавливает бит результата только в том случае, если оба бита были установлены, в противном случ.tе этот бит будет равен О. Изучите :Jти две опе­ рации, использ,Уя приведенную диаграмму: операнд! операнд2 результат операнд\ операнд2 результат бит 7 1 о о о 6 о о о 5 1 4 1OR1 1 1ЛND1 з о о о о о о 2 о о о о о о 1 о о о о о о о При программировании ИЛИ применяется для установки 'одного или более битов в ячей~е памяти или статусном регистре. Например, может возникнуть необход.имость установить атрибут мерцания определенному символу на экране терминала. Эта опе­ рация требует установки седьмого бита. Программа может просто записать весь байт атрибутов по нужному адресу, но состояние
497 остальных семи битов может быть неизвестным. Поэтому надо прочитать байт из нужного· места видеобуфера и поместить ero в • целую переменную, например Х. Затем готовится байт, у 1<отороrо установлен 1олько седьмой бит. Как вы знаете (или можете узнать из приложе11ия А), такой байт равен 128. Теперь просто запишите У=ХOR128ивУбудеттожезначение,чтоивХ,носуста­ новленным седьмым битом. Приведенная ниже диаграмма иллюст­ рирует эту операцию: бит атрибут 128 результат 7 о 1 1 6 1 о 1 5 .о о ·о 4 1 OR о 1 3 о о о 2 о о о 1 о о о о 1 о . 1 В числе 128 только седьмой бит vстановлен; Независимо от того, был ли он установлен или нет у· байта атрибутов, oh ·будет установлен в результирующем байте. Что касается остальных семи битов, то они будут установлены в результирующем байте, только· если они уже были установлены в байте атрибутов. Операция ИЛИ может применяться для установки более чем' одноrо бита за один прием (но обратите внимание на предостерегающие заме­ чания, помещенные ниже). Чтобы установить· биты 2 и 3, надо использовать сумму значений этих двух битов: 4. + •8 • 12. бит атрибут 12 результат 7 'О о о 6 1 о 1 5 о о о 4 1 OR о .1 3 о 1 2 о 1 1 1 о о о о о Для сброса одного или более битов используется операция И. Для этого надо вычислить значение байта, у которого установлены все биты, за исключением того, который вы хотите сбросить. Пом­ ните, что все соответствующие биты должны быть установлены,
498 Приложения чтобы результирующий бит тоже был установлен. Чтобы сбросить бит 7, возьмите значение 255 - _128 127: бит атрибут 127 результат 7 1 о о 6 1 1 5 о о 4 1 AND 1 3 о о 2 о о о о о Отметим, что каждый бит, установленный в байте атрибутов (кроме бита 7), комбинируется с I в байтовом значении 127 и поэтому равен 1 и в результирующем байте. Б.иты, которые были равны О в байте атрибутов, остаются равными О. Иногда программе нужно установить группу битов (поле). HanpиJi.fep, вы хотите изменить три младших бита байта видео­ атрибутов, изменяя тем самым _цвет символа. Пусть новая цепочка битов будет 101. Ей соответствует значение 5, но выполнение опе­ рации ИЛИ с 5 может не привести к желаемому результату, поскольку ИЛИ устанавливает бит в результирующем байте, если хотя бы один из соответствующих битов был равен 1. Если сред­ ний бит был установлен в байте атрибутов, то он останется уста­ новленным и в результирующем байте: бит атрибут 5 ~зул1.н1т 2 о 1 1 1 OR о о о В таком случае программа должна сначала сбросить все три бита с помощью И, а затем установить нужные биты с помощью ИЛИ. В данном случае 255 - 4 - 2 - 1 = 248, поэтому сначала надовычислитьУ=ХAND248,азатемZ=УOR5. Не слишком сложно для программы и определит~,, установлен или сброшен определенный бит. Для этого надо произвести опера­ цию И с байrом, у которого сброшены все биты, кроме те,стиру­ емого (скажем, бита 5, который равен 32). Ес,1и результат отличен от нуля, то тестируемый бит был установлен:
Приложения 499 бит атрибут 32 результат 7 1 о о 6 о о о 5 1 1 4 1\.ND о о 3 о о о 2 о о о l о о о о о о Ну а что делать, когда программе требуется знать установку двух или более битов? Например, биты 6 и 7 могут хранить номер от О до 3, но если изолировать эти биты~ то они дадут в результате одно из четырех (десятичных) значений: О, 64, 128 и 192. Поскольку Бейсик вынуждает вас работать не с двоичными чис­ лами, то требуются особые приемы, чтобы определить, какому злачению соответствует данная цепочка битов. Чтобы позволить вам избежать этих манипуляций, приводятся две процедуры. Пер­ вая из них преобразует десятичное число, хранящееся в байте, в строку из восьми 1 или О. Отметим, что это символьная строка, а не двоичное число. Вторая процедура берет такую строку (любой длины) и преобразует ее в десятичное число. Используя эти про­ цедуры, вы можете легкр анализировать статусные байты в памяти. С помощью функции MID$ вы можете выделить битовое поле и преобразовать содержащееся в нем значение в десятичное число. Вот процедура преобразования десятичного числа в двоич­ ную строку: НЮ STATUSBYТE РЕЕК(13): GOSUB IООО'получаем байт из памяти и 1.10 PRINT ВIТРАТТЕRN$ ·выполняем процедуру преобра­ ·зования в двоичное число ·печатаем получи11шуюся строку 1000 '"преобразуем десятичное число в строку из 8 единиц и нулей • 1010 ВIТРАТТЕRN$ = "" ·чистим переменную 1020FORN=7ТООSTEP-1 'идем назад, наqиная с бита 7 1030 IF STAТUSBYТE - 2 "N < О ТНЕN 1060 'переход, если бит равен О 1040 ВIТРАТТЕRN$ = "l" + BITPAТТERN$ 'добавляем к строке 1 1050 STAТUSBYТE = STATUSBYТE - 2"N: GOTO 1070 1060 ВIТРАТТЕRN$ = "О" + BIТPAтq::RN$ ·добавляем к строке О
500 1070 NEXT 1080 REТURN Приложения •повторяем 7 раз Важно отметить, что порядок битов в двоичной строке обрат­ ный. Вместо тоrо чтобы двигаться слева направо от бита 7 к биту О, бит О расположен самым лев~м в строке. Причиной этоrо явля­ ется тот факт, что функция MID$ может легко выделить требу­ емые вам биты. Поскольку MID$ начинает отсчет с 1, то вы должны считать, что биты пронумерованы от 1 до 8. Чтобы выделить четвертый и пятый биты, напишите BITFIELD$ • " M1D$(BITSTRING,4,2). Затем определите десятичное значение (от О до 3), хранящееся в поле, используя процедуру обратноrо преобразования: 100 BIТFIELD$ = MID$(BITPAТТERN$,4,2): GOSUB 2000 110 PRINТ DECVALUE 'вырезаем часть двоичной строки ·печатаем десятичное значение поля 2000 '"преобразуем строку I и О в десятичное число 2010 DECVALUE = О ·чистим переменную 2020 FOR N = 1 ТО LEN(BITFIELD$) ·повторяем до конца поля 20,Ю DECVALUE = DECVALUE + VAL(MID$(BIТFIELD$,N,1)•2·(N-1) 2040 NEXT 2050 REТURN Прняоженне В. Основные сведения об языке ассемблера Читатель, не знакомый с языком ассемблера, скоро поймет, что многие программистские трюки не могут быть достиmуты другими средствами, Хотя изучение языка ассемблера требует отдельной книги, в этом приложении приводятся_ основные понятия, которые помогут новичкам разобраться в· примерах, написанных на этом языке. Внимательный просмотр разделов, посвященных среднему и низкому уровням, даст вам возможность получить представление о том, как работает ассемблер, после чеrо намноrо •л~е изучить разные частные вопросы. Здесь обсуж­ даются не все ассемблерные инструкщ,ш, встречающиеся в прог-
501 АХ !АН АЦ вх !ВН вц Регистры общего сх !СН сц наэначенм11 (8 мпи 16 бит) DX ,он 1( DЦ DI SI ВР, } Индексные регистры (только 16 бмт) SP Указатель стека IP Счетчмк команд Укаэатепм сегментов: cs кодового DS данных ES допопнитепьного ss стека Фпагм! ~ @1 l?J t?J • 1•:!"'"' 8 ... .., .. о ! 11ii :,: 8 :,: ! :,: ! .. cu cu cu i!.lc: с 7 i::: ,s 18. ' :11 ~с :i !s :i: с: gr::t rис. 8.1 раммах, но вы обнаружите, что О!(ОЛО 95 % инGТрiкций, встре­ ченных· вами в программах, описаны. здесь, а значение остальных может быть понято блаrода~я комментариям к п~граммам.
502 Приложения Микропроцессор 8088 имеет 13 16-разрядных регистров, у каж­ дого из которых есть свои функции. В то время как в языках высо­ кого уровня вы можете поместить два числа в переменные, а затем сложить эти переменные, то в языке ассемблера эти числа поме­ щаются в регистры микропроцессора, а затем складываются значе­ ния, содержащиеся в регистрах. Все операции в языке ассемблера состоят в обмене данных с регистрами, а затем выполнении опера­ ций на регистрах, таких, как изменение отдельных битов, выпол­ нение арифметических операций и т.д. Одной из причин высокой эффективности •языка ассемблера является хранение данных в регистрах микропроцессора; компиляторы имеют тенденцию воз­ вращать все значения в память после выполнения операции, а дос­ туп к памяти требует большого времени. На рис. B.l показаны 13 регистров микропроцессоров 8088 и 80286 (последний имеет дополнительные средства для многозадачной работы, которые мы не будем здесь рассматривать). Регистры АХ, ВХ, СХ и DX являются регистрами общего наз­ начения. Их особенность состоит в том, что операции могут произ­ водиться не только над содержимым всего регистра, но также и над половиной. Каждый из четырех регистров делится на старшую и младшую части, например, АН обозначает старшую половину регистра АХ, а AL - младшую. Точно так же ассемблерная прог­ рамма может иметь доступ к ВН, BL, СН, CL, DH и DL. Это свойство очень полезно, поскольку часто программе приходится работать с ~айтовыми величинами. Регистры ВР, SI и DI ·,·акже достаточно удобны, хотя они могут принимать только 16-битовые ,значения. Каждый бит регистра флагов сообщает о соответству­ ющем статусе процессора, например, о том, что при выполнении арифметической операции был перенос за разрядную сетку.. В общем случае значения помещаются в регистры с помощью инструкции MOV. MOV АХ,ВХ пересылает содержимое регистра ВХ в АХ, затирая ранее содержащееся в АХ значение. МОУ AH,BL приводит к пересылке байта из регистра в регистр, но МОУ AX,BL - недопустимая инструкция, так как значения должны иметь одинаковый размер. Инструкция МОУ может также передавать значения из памяти, например, МОУ AX,ACT_NUM. Здесь ACT_NUM - имя переменной, которую создал программист, совсем как в языке высокого уровня. Переменная создается оператором вида АСТ NUM DW О. Этот оператор оставляет место для слова (двух байтов), присваивая им значение О. Другие допус­ тимые символы в этом операторе это DD (для двойного слова) и DB (для байта или строк). Ассемблер следит за адресами • переменных, поэтому при ассемблировании оператора МОУ АХ,АСТ_NUM имя переменной зам~няется на ее адрес.
503 Работа с именами переменных - самый простой ·способ иденти­ фикации данных в программах на языке ассемблера. Но имеются различные способы хитрой адресации, которые позволяют прог­ рамме хранить массивы или использовать указатели. Например, MOV АХ, [ВХ Ir~J ! гю,·1,1 1:1l.'т в АХ значение, которое содержится по смещению, равному t:уммс значений регистров ВХ и SI. Но от чего отсчитывать смещение? Ответ заключается в том, что все данные собраны в одну часть программы, а весь исполняемый код - в другую. Часть, отведенная под данные, называется сегментом данных, а под программу - ко,:ювым сегментом. Все переменные, отведенные. для хранения данных, адресуются через смещение относительно начала сегмента данных. Позиция в памяти, с которой начинается сегмент данных, хра­ нится в регистре DS, одном из четырех сегментных регистров. Как и все остальные регистры микропроцессора, он 16-разрядный, поэтому не может содержать числа, большие чем 65S35. Каким же • образом сегмент даных может указывать на ячейки памяти, распо­ ложенные п верхней части меrабайтного адресного пространства? Ответ состоит в том, что сегментные регистры автоматически умножаются на 16, а результат указывает на место в памяти, с которого начинается сегмент. Таким образом, сегменты всегда выравнены на 16-байтовую границу. После того как сегмент уста­ новлен, все остальные регистры могут содержать смещения, указы­ вающие на любой из следующих 65535 байт. Регистр дополни­ тельного сегмента (ES) также исп0льзуется для указания на дан­ ные, хранящиеся в памяти. Среди ассемблерных инструкций, которые вы часто будете встречать в этой книге, есть инструкции загрузки сегментных и относительных адресов переменных. MOV AX,SEG ЛСТ NUM помещает значение сегментного регистра, в котором расположен ЛCT_NUM в АХ, а впоследствии это значение будет переслано в DS. MOV BX,OFFSET ЛСТ NUM помещает в ВХ смещение пере­ менной ЛСТ NUM в сегмёнте данных. После выполнения ::>тих операций DS:ВX будут указывать на ЛСТ_NUM. Если АСТ _NUM • является одномерным массивом, то для указания на определенный элемент массива может использоваться добавочное смещение. Вы часто будете встречать также инструкцию LEA, предоставляющую друrой способ загрузки смещения. Кодовый сегмент содержит последовательность машинных инструкций, составляющих программу. Например, инструкция MOV существует в виде нескольких байтов машинно~::о кода, зна­ чение байтов которого определяет, в какой регистр идет пересылка и откуда. Регистр IP (счетчик команд) содержит величину смеще­ ния, которая указывает на ту инструкцию в кодовом сегменте, которая сейчас должна выполняться. После выполнения инструк-
504 ПриJ1ожения ции IP увеличивается таким образом, чтобы он указывал на следу­ ющую инструкцию. В простейшей программе счетчик команд будет передвигаться от первого байта кодового сегмента к послед­ нему, где программа и завершится. Но, как и другие программы, программа на языке ассемблера может быть разбита на процедуры (подпрограммы), поэтому счетчик команд может прыгать из одного места кодового сегмента в другое. Когда счетчик команд прыгает в другое место кодового сег­ мента, то его старое значение должно быть запомнено, с тем чтобы можно было вернуться в нужное место, как это делает оператор RETURN в Бейсике, возвращая управление в то место, откуда была вызвана процедура. В языке ассемблера процедуре присваивается имя, например СОМВ DAT, и оператор CALL COMB_DAT передает управление в процедуру. Процедура завершается инструкцией RET (возврат). При вызове процедуры процессор запоминает текущее значение счетчика команд, складывая его в стек. Стек - это область, используемая для временного хранения данных. После завершения процедуры старое значение с•1етчика команд берется из стека и выполнение программы продолжается. Стек также содержится в отдельном сегменте, который, совер­ шенно естественно, называется сегментом сте!(а. Ему соответствует сегментный реmстр SS. В регистре SP хранитсs1 укiiзател~, стека, который всегда указывает на вершину стека и изменяется при засылке на стек и выборке из стека. На первый взгляд стек кажется достаточно неуклюжим спосо­ бом хранения информации, но у него есть два преимущества. Во­ первых, доступ к его с,одержимому намного быстрее, чем к пере­ менным, хранящимся в· памяти, а во-вторых, стек может использо­ ваться и для друmх целей. Он может хранить адреса возврата из процедуры, вложенной в другую процедуру. Впоследствии то же самое пространство может использоваться программистом д..ля хра­ нения данных, которые должны сейчас обрабатывап,ся, но для которых не хватает места в реmстрах микропроцессора. Прог­ рамма складывает содержимое регистра в стек командой PUSH, а позднее забирает его оттуда командой РОР. В ассемблерных прог­ раммах, приведенных в этой книге, вы нс раз встретитсс~, с инст­ рукциями типа PUSH ВХ и РОР DX. Неправил~,ный порядок обмена данными со стеком - лучший способ привести асссмб­ дерную программу к сбою. После того как программист на языке ассемблера установил три сегментных регистра (CS, DS и SS) и загрузил данные в регистры микропроцессора, в его распоряжение предоставляется широкий набор встроенных средств, с помощью которых процессор может облегчить работу. Вот наиболее рас:простравенные из них:·
ADD АХ,ВХ MUL ·BL INC BL • LOOP ХХХ OR ~.BL SHL AX,l IN AL,DX JMP СМР AL,BL ТЕSТ AL,BL MOVS sos Прибавляет ВХ к АХ. Существует еще инструкция вычитани11 (SUВ), а также варианты обеих этих инструкций. Умножает BL на АХ. Имеется еще инструкция деления (DIV), а , также варианты обеих этих инструкций. Увеличивает BL на 1. Имеется также инструкция уменьшения (DEC) . Возвращает программу назад к строке, помеченной ХХХ, повто-. рая процесс столько раз, какое число содержится в СХ (анвло-­ гично инструкции FOR " ТО .. NEXT в Бейсике). Выполняет опера'цию логического ИЛИ над содержимым реrис­ тров AL и BL, прич-ем результат помещается в AL. ИмеЮТС!I та!О!<е инструкции AND, XOR и NOT. • Сдвигает все биты, содержащиеся в АХ, на одну позицию вnе80. Это :жвивалентно умножению еодержимоrо АХ на 2. Друrие инструкции сдвигают биты вправо или осуществляют циiс.llи­ ческий сдвиг. Все они необходимы ДJ\11 битовых операций; таких, как устано~ка точек экрана. Помещает в АХ байт, обнаруженный в порте, адрес котороrо указан в DX. Имеется также инструкция ОUТ. Передает управление в другое место программы, как инструкция GOTO в Бейсике. JMP УУУ передает управление на строку ороr­ раммы, имеющую метку УУУ. Сравнивает содержимое AL и BL. За инструкцией СМР обычно следует инструкция условного перехода. Например, если за инструкцией СМР следует инструкция JGE, то переход про­ изоl'дет, только если BL больше или равно AL. Инструкция СМР достигает тt1ro же результата,· что и инструкция IF .. ТНЕN • Бейсике (на самом деле инструкция IF .. ТНЕN, переводи-кв интерпретатором Бейсика в инструкцию СМР). • Проверяет, есть 'ли среди битов, установленных в BL, такие, которые установлены также и в AL. За этой инструкцией обычно следует команда условного перехода, так же как за СМР. ТЕSТ очень полезен при проверке статусных битов (битовые операции очень просто реализуются в языке ассемблера). Пересылает строку, длина которой содержится в· СХ, с места, указываемого SI, на место, указываемое DI. Имеется еще несколько других инструкций, св11занных с пересьu1кой и поиском строк. Язык ассемблера обеспечивает несколько вариантов этих инст­ рукций, а '{акже ряд дру111х специальных инструкций. К тому же имеется. целый масс инструкций, называемых nсевдооператорам.и, которые помещаются. в текст программы с целью указания ассем­ блеру, как обрабатывать данную программу. Например, один из типов псевдооператоров автоцатически вставля.~ часто использу-
506 Приложения емый кусок кода по всей программе. Такая порция кода называ­ ется макросом, и именно эт.о свойство ассемблера дало ему назва­ ние "макроассемблер". И наконец, ассемблер предоставляет возможность, которой завидуют (или, по крайней мере, должны _завидовать) все, кто программирует только на языках высокого уровня. Имеется в виду возможность оптимальным образом использовать прерывания опе­ рационной системы. Ведь это не что иное, как готовые процедуры. Однако вместо того, чтобы вызывать их по CALL, они вызываются инструкцией INT. INT21H вызывает прерывание с шестнадцати­ ричным номером 21. Имеется ряд таких прерываний, как в базо­ вой системе ввода/ вывода ПЗУ, так и в операционной системе, причем некоторые из этих процедур необычайно мощны. На самом деле некоторые из них настолько тесно связаны с системой, что вы практически не можете сами написать эквивалентную процедуру. Языки высокого уровня позволяют использовать многие из этих прерываний. Они применяют их для вывода на экран, приема ввода с клавиатуры и доступа к дискам. Но многие действительно полезные прерывания игнорируются языками высокого уровня, например, такие, которые позволяют запустить из одной прог­ раммы другую. Некоторые трансляторы (такие, как Lattice С иJJи TurЬo Pascal) предоставляют доступ к этим прерываниям, если вы знаете, как их подготовить, и вы можете использовать для :этой цели разделы среднего уровня данной книги. Перед вызовом прерывания некоторая информация должна быть помещена в регистры процессора. Например, пр'ерывание, вертикально сдвигающее экран, должно знать размеры сдвигаемого окна, число строк, на которое его надо сдвинуть, и т.д. Эти значе­ ния часто называют входными регистрами. Снова и снова вы будете встречать слова "при входе ВХ должен содержать ... ", опи­ сывающие спецификацию входных регистров. Аналогично при возврате из прерывания некоторые регистры возвращают значения или статусную инфор-.,:ацию. Они называются выходными регис­ трами, и мы описываем их словами "при выходе АХ содержит ... " . Зачастую одно прерывание содержит много функций. В частности, операционная система поместила практически все свои возмож­ ности в прерывание 21Н. Поэтому·при вызове прер1,1вания необхо­ димо указывать номер функции. Все прерыв,tния (как BIOS, так и DOS) передают номер функции в АН (иногда в AL содержится номер подфункции). • Все ранее сказанное в основном служит только для того, чтобы дать вам первое представление о предмете. Но если вы будете вни­ мательно просматривать простейшие примеры, содержащиеся в этой книге, то поймете стоящую за ними логику. Язык· ассемблера имеет репутацию трудного языка. Но это утверждение не имеет за
Приложения 507 собой веских оснований. Существует достаточно сложностей и в языках высокого уровня. И если ошибки в ассемблерной прог­ рамме бывает очень сложно обнаружить, то в основном это свя­ зано с тем, что сам текст программы намного длиннее, чем эквивалентный текст на языке высокого уровня (однако ассемблерный код намного компактнее). В настоящее время многие профессионалы пишут программы на языке Си, затем анализируют эффективность и переписывают критические кусочки программы, которые расходуют много времени, на языке ассемблера. Невозможность написания таких ассемблерных процедур может иногда свести усилия программиста к нулю. Поэтому найдите хороший учебник для начинающих по ассемблеру и приступайте! Возможнр, самой большой наградой для вас станет момент, когда вы, наконец, действительно станете понимать, как же работает компьютер. Прнложенне Г. Включенне ассемблерных процедур в проrраммы на Бенснке Процедуры на языке ассемблера состоят из строк байтов машинного кода. При выполнении процедуры Бейсик передает управление из последовательности инструкций, составля­ ющих программу на Бейсике, в то место, где хранятся инструк­ ции, которые могут быть декодированы в последовательность инст­ рукций языка ассемблера. При завершении ассемблерной про­ цедуры управление возвращается в то место бейсиковской прог­ раммы, откуда была вызвана процедура. В этой книге ассемблерные процедуры, используемые в прог­ раммах на~ Бейсике, приведены в двух видах. В обоих видах они включены в программу, а не хранятся как отдельный дисковый файл. При первом способе требуется, чтобы коды процедуры нахо­ дились в отдельном месте в памяти, а при втором, мене~ при­ нятом, этого не требуется. В первом способе процедура помещается в операторы DA ТА, и программа пересылается в неиспользуемую часть памяти, а затем вызывается оператором CALL. Надо позаботиться о том, чтобы код процедуры не накладывался на какие-либо данные, и наоборот. Обычное решение этой проблемы состоит в том, что процедура помещается в те адреса памяти, к которым Бейсик не может полу-
508 Приложения чить доступ. Поскольку интерпретатор Бейсика не может иметь доступ за пределы 64К, то для системы, скажем, с памятью 256К, нужно поместить процедуру в старшие 64К. Для систем с памятью 128К вы должны вычислить, сколько памяти требуется операци­ онной системе, Бейсику и драйверам устройств. Допустимо, чтобы они занимали 2SK плюс 64К, используемых Бейсиком. В системах с 64К применяйте при старте команду CLEAR, которая ограничи­ вает объем памяти, доступный для Бейсика. CLEAR,n ограничи­ вает Бейсик n байтами. Затем поместите процедуру в самые верх­ ние адреса памяти. Для указания начала области, куда будет • помещена процедура, используйте оператор DEF SEG, а затем с помощью оператора READ считываются байты процедуры и помещаются в память до тех пор, пока вся процедура не будет помещена на место. Например: 100 DATA &Нхх, &Нхх, &Нхх, &Нхх, &Нхх 110 DATA &Нхх, &Нхх, &Нхх, &Нхх, &Нхх •1О-байтовая процедура 300 ""помещаем процедуру в память 310 DEF SEG = &Н3000 320FORN=ОТО9 330 READ Q 340 РОКЕ N,Q 350 NEXT ·у~азышн:м 1ш об;1асть 1шм11ти ·д11я каждого из 10 байт ·ч1паем байт данных помещаем его 11 память После того как процедура загружена в память и вы хотите ее использовать; необходимо, чтобы последний оператор DEF SEG указывал ца начало процедуры. Затем присвойте целой перемен­ ной значение О и напишите оператор CALL с именем этой пере­ менной. Если процедуре передаются параметры, то они должны быть указаны в скобках в конце оператора CALL. Например: 500 DEF SEG = &Н3000 510 DOGS = 12 520 CATS = 44 530POSS=1 540CAS=О 550 CALL CAS(DOGS,CATS ,POSS> указываем на начало процедуры ·у нее 3 11араметра ·11а•1шшем 11ы1юл11еш1е с \-го байта IJblllOJllllleм процедуру Имеется намного более простой и экономичный способ созда­ ния ассемблерных процедур, который избегает проблемы распреде­ ления памяти. Надо просто создать процедуру в виде строковой переменной внутри программы. Каждый байт может быть закоди-
509 рован с помощью CHR$. Затем используйте функцию VARPTR • для определения положения этой строки в памяти. Смещение, по которому находится :па переменная, хранится в двух байтах, сле­ дующих за тем, на который укажет VARPTR (в первом байте содержится ДJIИна строки). Затем этот адрес используется для вызова процедуры. Обратите внимание на способ применения опе­ ратоw- DEF SEG для-указания на сегмент данных Бейсика, с тем чтобы: полученное смещение указывало на адрес строки для опера­ тора CALL. Например: 100 DEF SEG ·устанавливаем сеrмент на данные Бейсика 110 Х$ = "СНR$(В4) + ... " ·код процедуры 120 У = VARPТR(X$) получаем дескриптор строки 130 Z = РЕЕК(У + 1) + РЕЕК(У + 2)*256 • ·вычисляем ее адрес 140 CAILZ Многие значения, выражаемые через CHR.$0, могут быть представлены и в виде символов ASCII. Вы можете писать ROUT • CHR$(12) + "АВ" вместо, ROUT • CHR$O2) + + CHR$(65) + CHR$(66). На самом деле большинство символов ASCII могут вводиться путем нажатия клавиши Alt, набора· номера кода на дополнительной клавиатуре, а затем отпус~ания клавиши Alt. Однако для наших целей коды от О до 31 не могут ,быть введены таким образом. Прн11оженне Д. Нспо11ьзованне драйвера устройства ANSI.SYS ANSI.SYS - это небольшая программа, входя­ щая в состав .операционной системы, которая может быть загру­ жена в память, с тем чтобы увеличить возможности MS-DOS. Она не сделана частью COMMAND.COM с целью экономии памяти, когда • в ней· нет необходимости. Средства, предоставляемые ANSf.SYS, могут быть использованы для удобства программирова­ ния, но ойи могут также служить средством достижения некоторой программной совместимости с машинами, несовместимыми с IBM РС, но работающими в мs..:DOS. Этот драйвер не предоставляет никаких добавочных возможностей, которых нельзя было бы добиться другим образомj но он делает управление клавиатурой и терминалом более простым (и обычно более медленным). Все
510 Приложения свойства драйвера ANSI.SYS описаны в этой книге под соответ­ ствующим заголовком. ANSI.SYS может быть загружен только во время загрузки опе­ рационной системы. Начиная с версии 2.0, система автоматически ищет файл CONFIG.SYS, так же как и файл AUTOEXEC.BAT . Файл CONFIG.SYS содержит различные параметры, такие, как число создаваемых буфероl! для файлов. Но он содержит также и имена тех драйверов устройств, которые должны быть загружены и включены в COMMAND.COM. ANSI.SYS как раз и является таким драйвером. Надо просто включить в этот файл строку DEVICE = ANSI.SYS. Она может быть единственной строкой в файле. Для его создания можно воспользоваться командой СОРУ. Надо просто ввести с терминала такие строки: СОРУ CON: CONFIO.~YS «CR• DEVICE = ANSI.SYS «CR• «Fб• «CR• Нажатие клавиши Fб записывает символ Ctrl-Z <ASCII 26), отмечающий конец файла. .,,, • Прнnоженне Е. Набор ннструкцнй мнкропроцессора 8088 вычисления таблице: Число тактов, которо~ надо добавить для эффективного адреса, содержится в следующей Компоненты а11~а Операнды Такты (а) база или индекс [ВХ). [ВР). (DI), (S1) 5 (б) смещение метка иJ1н смещение 6 (в) база + индекс . LBX) (SIJ, [BXJ [DIJ 7 (BPJ (SI). (ВР) [DI) 8 (r) смещение + база или индекс [BX),[BP),IDl),(SIJ + смещ. 9 (д) смещение + база + индекс [ВХ) (SI), [ВХ) [DIJ + смещ. 11 (ВР) [SI]. [ВР) [DI) + смещ. 12
511 Необходимо добавить также 2 такта при пересечении сегмента~ :ОТ-времена инструкций: Инструкция Такты ~ АЛА 4 1 AAD 60 2 ААМ 83 \. AAS 4 1 ADC регистр, регистр 3 2 ADC регистр, память 9(13) + ЕЛ . !-4 ADC память, регистр 16(24) + ЕЛ 2-4 ADC регистр, значение 4 3-4 ADC память, значение 17(25) + ЕЛ 3-6 ADC аккумулятор, значение 4 2-3 ADD регистр, регистр .3 2 ADD регистр, память 9(13) .+ ЕА 2-4 ADD память, регистр 16(24) + ЕА 2-4 ADD регистр, значение .4 3-4 ADD память, значение 17<°25) + ЕА 3-6 ADD аккумулятор, значение 4 2-3 AND регистр, регистр 3 2 AND регистр, память 903) + ~л 2-4 AND память, регистр 16(24) + ЕА 2-4 AND регистр, значение 4 3-4 AND память, значение· 17(25) + ЕЛ 3-6 AND аккумулятор, значение 4 2-3 CALL близкая процедура 23 3 CALL далекая nроцедура 36 5 CALL словный указатель н памяти 29+ЕЛ 2-4 CALL .словный реrистр указатель 24• 2 CALL двухсловный указатель в памяти 51+ЕЛ 2-4 CBW 2 1 CLC 2 1 CLD 2 1 си 2 1 смс 2 1 СМР регистр, регистр 3, 2 СМР регистр, память 903) + ЕЛ 2-4 СМР память, регистр 903) + ЕЛ 2-4 СМР регистр, значе»ие 4 3-4 СМР память, значение 10(14) + ЕЛ 3-6 СМР аккумул11тор, значение 4 2-3 CMPS nриемш1к, источник 22(30) 1 CMPS (REP) приемник, источник 9 + 22(30) / 1)шпор 1 CWD 5 1 DAA 4 1 DAS 4 1 DEC словный регистр 2 1 DEC байтовый регистр 3 2 DEC память 15(23) + ЕА 2-4
512 Инструкци11 DIV байтовый регистр DIV СЛОВНЫЙ регистр DIV байт пам11Ти DIV CJJoвo памАТИ ESC значение, пам11ТЬ ESC значение, реrистр НLТ IDIV байтовый реrиi:тр IDIV CJIOIJHЫЙ реrистр IDIV байт пам11ТИ mrv слово пам11ТИ IMUL байтовый регистр IMUL славный регистр IMUL байт пам11Ти IMUL слово памllТИ IN аккуму.1111ТОр, байт значения IN аккумул11тор, DX INC CJIOIIНЫЙ регистр INC байтовый реrистр INC памllТь INТ 3 INT значение байта, отличное от 3 INТO IRET JCXZ коротка11 метка JMP коротка11 метка JMP 6.лизка11 l'о!етка JMP далека11 метка Jxxx короткая метка LAНF U>S словнwй реrистр, двойное слово памяти LEA СЛОIIНЫЙ реrистр, слово 1шм11ти . LES словный реrистр, двойное СJЮВО памяти LOCK WDS строка-источник WDS (REP) строка-источник WOP коротка~ метка WOPE короткая метка WOPNE коротка11 метка WOPNZ коротка11 метка WOPZ коротка11 метка MOV паМ11ть. апумул11тор MOV аккумул11ТОр, пам11ть MOV реrмстр, регистр MOV реrмстр, пам11Т~. MOV nам11ть, реrистр MOV реrистр, значение ' .. моv значение, репtСТр MOV сеrментный реn1стр, словный ре1-истр MOV сеrментный регистр, с1юно nамяп1 Приложения Такты Байты 80-90 2 144-162 2 (86-96> + ЕЛ 2-4 (154-i 72) + ЕА 2-4 8(12) + ЕЛ 2-4 2 2 2 1 101-112 2 165-185 2 (107-118) + ЕЛ 2-4 (175-194) + ЕЛ 2-4 80-98 2 128-154 2 (86-104) + ЕЛ 2-4 038-164) + ЕЛ 2-4 10(14) 2 802) 1 2 1 3 2 15(23) + ЕЛ 2-4 52 1 51 2 53 НJIН 4 1 32 1 18шш6 2 15 2 15 3 15 5 16ИJШ4 2 4 1 24+ЕЛ 2-4 2+ЕЛ 2-4 24 + ~,:л 2-4 2 1 12(16) 1 9 + 13(17)/nо11тор 1 17или5 2 18НJШ6 2 19 ИJIН 5 2 19или5 2 18или6 2 10(14) з 1004) 3 2 2 8(12) + ЕЛ 2-4 9(13} + ЕЛ 2-4 4 2-3 1004) + 1::Л з 2 2 8(12) + ЕЛ 2-4
513 Инструкции Такты fuiliш моv· словный регистр, се1·ме1п111,1й ре,·ш:тр 2 2 . MOV слово пам11ти, се1·ме11т11ый реr•1стр 9(13) + ЕЛ 2-4 MOVS приемник, исто•1ник 18(26) 1 MOVS (REP) приемник, источник 9 + 17(25)/понтор 1 MUL байтовый регистр 70-77 2 " MUL СJIОВНЫЙ реrистр н&-133 2 MUL байт памитн (76-83) + ЕА 2~4 MUL слово пам11ти (128-143) + ЕЛ 2-4 NEG регистр 3 2 NЕG·пам11ть 16(24) + ЕА 2-4 NOP 3 1 NOT регистр 3 2 NOT памить 16(24) + ЕЛ 2-4 OR регистр, регистр 3 2 OR регистр, память 9(13) + ЕЛ ·2 -4 OR .пам11ть, регистр 16(24) + ЕЛ 2-4 OR реrистр, значение 4· 3-4 OR память, значение 17(25) + ЕЛ 3-6 OR аккумулитор, ~начение • 4 2·3 OUT байт значении, аккумулитор 10(14) ·2 OUT DX, аккумулятор 8(12) ,! РОР регистр 12 1 РОР сегментный регистр 12 1. Р.ОР память • 25+ЕА 2-4 POPF 12 1 PUSH реrистр 15 1 PUSH сегментный pemc1·p 14 1 ·/ PUSH памить ~"4 + ЕА· 2-4 PUSHF 14 1 RCL реrистр, 1 2 2 RCL реl:'4СТР, CL 8 +·4/l.i•11· 2 RCL памить, 1 . 15(23) + ЕЛ 2 RCL памить, CL 20(28) + ЕЛ + 4/бит 2 RCR регистр, 1 2 2 .RCR регистр, CL 8 + 4/бит 2 gcR памить, 1 15(23) + ЕА 2 RCR память, CL 20(28) + ЕА + 4/бит 2 REP- 2 1 REPE 2 1 REPNE 2 1 REPZ 2 1 REPNZ 1 2 1 RET (внутрисеrментный, без РОР) 20 1 . R E T (внутрисеrментный, с РОР) 24 3 RET (межсеrменmый, без РОР) ~~ 1 RET (межсеrменmый, с РОР) 3 ROL. ре-rистр, 1 2 2 ROL регистр, CL 8 + 4iбит 2 ROL память, 1 15(23) + ЕА 2 ROL память, CL 20!28) + ЕЛ + 4/бит 2
514 Приложения Инструкция ·1·акт111 l)айты ROR регистр, 1 2 2 ROR регистр, CL 8+4/бит , 2 ROR память, 1 15(23) + ЕЛ 2 ROR память, CL 20(28) + ЕЛ + 4/бит 2 SAHF 4 1 SЛl, регистр, 1 2 2 SЛL регистр,СL 8 + 4/бит 2 SAL память, 1 15(23) + ЕЛ 2 SAI, память, CL 20(28) + ЕЛ + 4/бнт 2 SAR регистр, 1 2 2 SAR регистр, CL 8 + 4/бит 2 SAR память, 1 15(23) + ЕЛ 2 SAR память, CL 20(28) +ЕЛ+ 4/бит 2 SBB регистр, регистр 3 2 SBB регистр, память 9(13) + ЕЛ 2-4 SBB память, регистр 16(24) + ЕЛ 2-4 SBB регистр, значен~1е 4 3-4 SBB память, значение 17(25) + ЕЛ 3-6 SBB аккумулят9р. значение 4 2-3 SCAS приемник .,, 15(19) 1 SCAS (REP) приемник 9 + 15( 19)/повтор 1 SHL регистр, 1 2 2 SHL регистр, CL 8 + 4/бнт 2 SHL память, 1 15(23) + ЕЛ 2-4 SHL память, CL 20(28) + ЕЛ + 4/бнт 2-4 SHR регистр,' 1 2 2 SHR регистр, CL 8 + 4/бит 2 SHR память, 1 15(23) + ЕЛ 2-4 SHR память, CL 20(28> +ЕЛ+ 4/бит 2-4 STC 2 1 STD 2 SТI 2 STOS приемник 11 (15) l STOS (REP) приемник 9+ 10O4)/rю~пор 1 SUB ,регистр, регистр 3 2 sun регистр, память 9(13) + ЕЛ 2-4 sun память, регистр 16(24) + ЕЛ 2-4 SUB регистр, значение 4 3-4 SUB lilaMЯTb, значение 17(25) + ЕЛ 3-6 SUB AL, значение 4 2-3 TEST регистр, регистр 3 2 TEST регистр, память 9(13) + ЕЛ 2-4 , TEST регистр, значение 5 3-4 TEST память, значение 11+ЕЛ 3-6 TEST AL,' значение 4 2-3 WAIТ 3+5n 1 XCNG AL, .словный рег~1стр 3 1 XCNG память, регистр 17(25) + ЕЛ 2-4 -XCNG регистр, регистр 4 2 XLAT таблиu,а-источник 11 1
Приложения Инструкция XOR регистр, регистр XOR регистр, память XOR память, регистр XOR регистр, значение XOR ~амять, значение XOR AL, значение Такты 3 9(13) .+ ЕА 1'6(24) + ЕА 4 17(25) + ЕА 4• 515 2 2--4 2-4 3-4 3-6 2 Прнложенне Ж. Набор Йнструкцнн мнкропроцессора 80286 Придерживаясь схемы, принятой в данной книге, здесь перечислены инструкции только для режимов реаль,­ ной адресации. Более мощный микропроцессор 80286 не требует добавочного времени на вычисление эффективных адресов, нет также отличия в выполнеllИи команд над байтовыми и словными переменными. Звездочка указывает, что вы должны добавить один такт, если при вычислении смещения суммируются три элемента. Буква m указывает число --байтов следующей' инструкции, а n - число повторений. Инструкция Тактьt Байты АЛА 3 1 AAD 14 2 ЛАМ 16 2 AAS 3 1 ADC регистр/память с регистром 2,7* 2 ADC значение с регистром/памятью 3,7* 3 'ADC значение с аккумулятором 3 2 ADD регистр/память с регистром 2,7* 2 ADD значение с регистром/памятью 3,7* 3 ADD значение с аккумулятором 3 2 .\ AND регистр/память с регистром 2,7* 2 AND значение с регистром/памятью 3,7* 3 AND значение с акку~улятором 3 2 CALl прямой внутри сегмента 7+m 3 CALL косвенный через регистр/память 7+m,ll +m* 2 внутри сегмента CALL прямой между сегментами 13+m 5 CBW 2 1 CLC 2 1 сш 2 1 cu 3 1 · 17*'
516 Инструкция смс СМР регистр/память с регистром СМР регистр с реrнстром/памятью СМР. значение с регистром/памятью СМР значение с аккумулятором CMPS повторенный СХ раз CMPS <iайт или слово CWD ,DAA DAS DEC регистр/память DEC регистр DIV байтовый реrнстр DIV словный реrнстр DIV байт памяти DIV слово памяти ESC HLT IDIV байтовый реrнстр IDIV словный регистр IDIV байт памяти IDIV слово памяти IMUL байтовый регистр IMUL словный регистр IMUL байт памяти IMUL слово памяти IMUL умножение на целое значение IN фиксированный порт IN переменный порт _ I NC реrиетf)/память INC регистр INS строка INS байт или слово INТ указанный mп INТ тип 3 INТO IRET JCXZ JМР короткий/длинный . J MP прямой внутри сегмента JMP косвенный через регистр/память JMP прямой между сегментами Jxxx LAНF ws LEA LES LOCK 'LODS LODS повторенный СХ раз Приложения !!!Ш:! Байты i 1 2,6* 2 2,7* 2 3,6* 3 3 2 5+9n 2 8 1 2 1 3 1 3 1 2,7* 2 2 1 14 2 22 2 17* 2' 25*. 2 9-20* 2 2 1 17 2 25 2 20* 2 28* 2 13 2 21 2 ·1 6* ·Z 24* 2 21,24* 3 5 2 5 1 2,7* 2 2 1 5+4m 2 5 1 23+m 2 23+·ш 1 24+ШЮIИ3 1 17+m 1 8 + mили4 2 7+m 2 7+m 2 7+m,11+m*, 2 11+m 2 7 + mили3 2 2 1 7* 2 3* 2 7* 2 о 1 5 1 5+4n 1
Приложения Инструкция LOOP LOOPZ/LOOPE LOOPNZ/LOOPNE MOV регистр в регистр/память MOV регистр/пам11ть в регистр MOV значение в регистр/память MOV значение в регистр MO'V память в аккумулятор MOV аккумулятор в память MOV реrистр/память в сегментный регистр MOV сегментный регистр в регистр/пам·ить MOVS байт или слово MOVS повторенное СХ раз MUL байтовый реrистр MUL с,ловный реrистр МUL'байт памяти MUL слово памяти NEG NOT регистр/память OR регистр/память с регистром OR значение с регистром/памятью OR зна~ение с аккумулятором ОUТ фиксированный порт OUT переменный порт OUTS строка OUTS байт или слово РОР память РОР регистр РОР сегментный регистр РОРА POPF PUSH память PUSH регистр PUSH сегментный регист~} PUSH ;шачение PUSl:IA PUSHF RCA регистр/память на 1 RCA реrистр/пам11ть на СХ RCA реrистр/память на число RCR регистр/память на 1 RCR регистр/память на С:Х: RCR регистр/память на число RET внутри сегмента RET внутри сегмеmа, добавляя значение к SP RET между сегментам•• RET между сегментами, добавляя значение к SP • ROL регистр/паМIIТЬ на 1 517 !!!ш Байты \ 8+4nили4 2 8+4nили4 2 8+4nили4 2 2,3* 2, 2,5* 2 2,3* 3 2 2 5 3 3 3 2,5* 2 2,3* 2 5 1 5+4n 2 13 2 21 2 16* 2 24* 2 2 2 2,7* 2 2,7* 2 3,7* 3 3 i 3 2 3 11 5+4m 2 5 1 5* 2 5 1 5 1 19 I' 5 1 5* 2 3 1 3 1 3 2 17 1 3 1 2,7* 2 5+n, ~+п* 2 5+n, 8+п* 3 2,7* 2 5+n, 8+п* 2 5+n,8+n* 3 11+m 1 11+m 3 15+m 1 15+m 3 2.7* 2
518 Приложения Инструкция Такты Байты ROL регистр/память на СХ 5+п,8+п* 2 ROL регистр/память на число 5+n,8+n* 3· ROR реrистр/память на 1 2,7* 2 ROR регистр/память на СХ 5+n,8+n* 2 ROR реrистр/память на число 5+n,8+n* 3 SAНF 2 1 SAL регистр/память на 1 2,7* 2 SAL регистр/память на СХ 5+n,8+n* 2 SAL регистр/память н~ число 5+n,8+n* 3 SAR регистр/память на 1 2,7* 2 SAR регистр/память на СХ 5+n,8+n* 2 SAR регистр/память на число 5+n,8+n* 3 SBB регистр/память с регистром 2,7* 2 SBB значение с регистром/памятью 3,7'1" 3 SBB значение с аккумулятором 3 2 SCAS повторенное СХ раз 5+8n 2 SCAS байт или слово 7 1 SEG (переопределение сегмента) о 1 SHL регистр/память на 1 • 2,7* 2 SHL регистр/память на СХ 5+n,8+n* 2 SHL регистр/память на число 5+n,8+n* 3 STC 2 1 STD 2 1 STI 2 1 STOS повторенное СХ раз 5+3n 2 STOS 3 1 SAL регистр/память на 1 2,7* 2 SAL регистр/память на СХ 5+n,8+n* 2 SAL регистр/память на число 5+n,8+n* 3 SUB регистр/память с регистром V 2,7* 2 SUB значение с регистром/памятью 3,7* 3 SUB значение с аккумулятором 3 2 TEST реrистр/память с регистром 2,6* 2 1 TEST значение с регистром/памятью 3,6* 3 TEST значение с аккумулятором 3 2 WAIТ 3 1 XCNG регистр/.память с регистром 3,5* 2 XCNG регистр- с аккумулятором 3 1 XLAT 5 1 XOR регистр/память с регистром 2,7* 2 XOR значение с регистром/памятью 3,7* 3 XOR значение с аккумулятором ·3 2
Глоссарий 146818. Микросхема в АТ, содержащая часы реального времени ~ информацию о конфигурации. 6845. Микросхема контроллера дисl!Jlея. 76496. Микросхема синтезатора звука PCjr. 765 (~D765). Микросхема контроллера НГМД. 8048. Микропроцессор кл-,виатуры. 8237. Микросхема прямого доступа к .памяти <DMA). 8250. Микросхема коммуникационного адаптера.· 8253. Микросхема программируемого таймера. •8255. Микросхема адаптера интерфей~а с периферией. 8259. Микросх~ма контроллера црерываний. 8087. Микросхема математического сопроцессора на РС, ХТ и PCjr. 8088. Центральный процессор у РС, ХТ и PCjr:" 80286. Центральный процессор у АТ;- 80287. Микросхема математического сопроцессо~t на АТ. AND(И). Логическая операция, сравнивающая значения двух цепочек битов и на этой основ·с создающа_я третье значение, где установлены только те биты, которые были установлен1,1 в обоих значениях компонентов. 1 ANSI.SYS. Драйвер устройства,. поставляемый в~есте с операцион­ ной системой, 'который способен выполнять многие функции BIOS. Он применяется для достижения программной совмес­ тимости с отличными от IBM РС машинами, использующими MS-DOS. AUTOEXEC.BAT. Имя командною .Файла, котор1>1й ·автоматически ',, выполняется при загрузке системы. '.. .. _, ~-- ~.
520 Глоссарий В. Суффикс, обозначающий число, представленное в щюичном виде, например 1011 l0l lB. См. приложение А. BIOS. Базовая система ввода/ вывода, которая является частью операционной системы, постоянно хранящейся в ПЗУ машины. CD. "Носитель обнаружен". См. DCD. СОМ. Тип исполняемого файла, в котором привязка уже выпол­ нена и поэтому все адреса правильно записаны в файле перед его загрузкой. CONFIG.SYS. Имя специального файла, который система просмат­ ривает при загрузке. Этот файл содержит информацию о параметрах системы и драйверах устройств, которые должны быть установлены, что позволяет задать требуемую конфи­ гурацию системы. €PU. Центральный процессор, который выполняет инструкции, составляющие компьютерную программу. У всех IВМ РС центральным процессором является микросхема 8088, за исключением РС АТ, у которого процессором служит микросхема 80286. ,CRC. См. цюслический контроль четности. CR/LF. Возврат каретки/перевод строки. Эта пара символов используется, чтобы вызвать· перевод курсора или печата­ ющей головки к началу следующей строки. CRT. Электронно-лучевая трубка, т.е. видеодисплей. CTS. Очистка посылки. Сигнал от модема порту коммуникации, индицирующий, что модем готов начать передачу данных. Он является частью процедуры установления связи. 1 DB. Термин языка ассемблера, указывающий, что объект данных имеет размер l байт, или что это строка, состоящая из одно- байтовых кодов. • DCD. Обнаружен носитель данных. Сигнал от модема порту ком­ муникации, индицирующий, что установлена связь с другим модемом. DD. Термин языка ассемблера, индицирующий, что объект данных имеет длину 4 байта. DMA. См. прямой доступ к памяти. DSR. Готовность набора данных. Сигнал коммуникационному порту от модема, индицирующий, что модем готов.
521 DTA. Область обмена с диском. Буфер, используемый для обмена с диском, при доступе методом управляющего блока файлn. DTA по умолчанию. Область переноса данных размером 128 байт, которая выделяется каждой программе и начинается со сме­ щения 801-I в префиксе программного сегмента. DTR. •Готовность приемника данных. Сигнал от коммуникацион­ ного порта к модему, индицирующий, что компьютер готов. DW. Термин языка ассемблера, указывающий, что объект данных имеет длину 2 байта. • EOF. Сокращение для "конца файла".· Еsс-последовательность. Управляющая строка, начинающаяся с символа Esc (ASCII 27). Например, ?ольшинство уnравля­ , ющих команд прин:тера выполняется с помощъю Еsс-после­ довательностей. ЕХЕ. Исполняемый файл, который требует привязки при загрузке. Не ~се адреса программы могут быть установлены до тех пор, пока неизвестно ее положение в памяти. ЕХЕ-файлы, имеют заголовок, который содержит информацию об этой привязке. Эти файлы загружаются немного дольше и тре­ буют больше места на диске, чем файлы типа СОМ. ЕХЕС. Функция опе1к1ционной системы, позволяющая программе запустить другую программу. Она может также загружать оверлеи. FAT. См. таблица размещения файлов. FCB. См. управляющий блок:' файла. Н. Суффикс, обозначающий число, представленное в шестнадцати­ ричном виде, например ОDЗН. См. приложеиие А. IOCTL. Управление вводом/выводом. Этm: механизм, предост.1в:­ ляем1,1й системой, позволяет программе взаимодействовать с драйвером устройства, прямо посылая и получая. управля­ ющие строки, а не включая их в поток данных, riосыл.tемых драйверу устройства. IRQ. Сокращение для "запроса на прерывание". Используется при ссылке на маскируемые аппаратные прер1>1вания. LSB. Младший бит или младший байт. MSB. Старший бит или старший байт. OR (ИЛИ). Логическая операция, сравнивающая значения двух цепочек битов и создающая третье значение, . у которого
522 Глоссариii установлены все биты, установле}!_ные хотя бы у одного из компонентов. PSP. См. префикс программного сегмента. - RI. Индикатор звонка. Сигнал от модема с автоответчиком порту коммуникации, сообщающий, что телефон, с которым· свя­ зался мо~ем, звонит. ROM-BIOS. См. BIOS. RTS. Запрос на· посылку. Сигнал от коммуникационного порта к модему, указывающий, что компьютер хочет, чтобы были посланы данные. SETBLOCK. Функция операционной системы, которая сокращает • ми увеличивает область памяти, •отведенной данной программе. XON/XOFF. Ме;rод установления связи при последовательной связи, который использует коды ASCII 17 и 19 дЛЯ сигнала передающей станции, что она должна соответственно возоб­ новить или приостановить передачу. Они используются, когда данные поступают слишком быстро и программа не успевает их обработать. XOR (исключающее ИЛИ). Логическая операция, сравн:цвающая битовые цепочки двух значений и создающая третье значе­ ние, где установлены только те биты, для которых только один из сравниваемых битов был устанрвлен. Абсолютные •координаты. Координаты, . указанные относительно центральной оси, а не относительно предыдущих исполь­ зуемых координат (относительные координаты). Абсолютные сектора диска. Под "доступом к абсолютному сектору диска" . понимается чтение сектора, занимающего • определенное положение на диске. Абсолютный адрес. Адрес памяти, выраженный в виде смещения ' относительно младшего адреса (0000:0000), а не относи­ тельно какого-либо определенного смещения в памяти (отно­ сительный адрес). Адрес порта. Число в диапазоне от О до 65535, которое адресует порт. Адреса портов отделены от адресов памяти. Доступ к портам осуществляется с помощью инструкций IN и OUT в языке ассембле.ра и INP и OUT в Бейсике.
,523 Адресация. Средство доступа к определенным ячейкам памяти :3а счет указания либо их абсолютного положения, либо относи-· тельноrо смещения. Адресный регистр. Регистр одной из вспомогательных микросхем, который служит в качестве указателя на один из нескольких регистров данных микросхемы, доступ к которым осущест­ вляется через один порт. Программа должна сначала индек­ сировать регистр, посылая номер интересующ~rо регистра в адресный регистр. Аnпаратное прерывание. Прерывание, вызываемое оборудованием, т.е. одним из периферийных устройств, микросхемой под­ держки или самим процессором. Аппаратный сдвиг экрана.,- Метод вертикального сдвига изобра­ жения на дисплее, основанный на изменении стартовой точки видеобуфера, а не на сдвиге содержимого буфер.:'!. Асинхррнная связь. ПОС:Ледовательный канал связи, в котором время между посылкой символов может быть переl\1енным. Ассемблер. Программа, преобразующая текст программы на языке ассемблера в машинный код. Атрибут. Характеристика, приписываемая устройству или данным. Каждый символ текстовоrо экрана имеет атрибуты, опреде­ .1,1яющие ero цвет, интенсивность и т.д. Драйверы устройств имеют атрибуты, определяющие способ обработки данных, 1 управляющие строки и, т.д. Файлы могут иметь атрибуты, указывающие, что они являются скрытыми, только для чтения и т.д. Атрибуты файла. Поле элемента каталога файлов, определяющее статус файла и делающее ero обычным, скрытым, только для чтения и т .п. 1 Атрибуты цвета. Цепочки битов, хранимые в видеобуфере, кото­ рые определяют ~вет определенной точки или символа на экране. Для монохромноrо и цветного адаптера эти атрибуты совпадают с системой кодовых номеров цвета. Однако для PCjr и EGA они относятся к номеру регистра палитры, а уже ·этот регистр содержит код цвета, с которым связан этот атрибут. _,,; Базовый адрес. Младший из группы смежных адl'есов портов, через которые осуществляется доступ к периферийному устройству.
524 Глоссарий Байт атрибутов. Вообще говоря, байт, содержащий код, устанав­ ливающий специальные характеристики среды, к которой он относится. Байт атрибутов файла (в дисковом каталоге) определяет статус скрытого файла, статус только для чтения и т.п. В буфере дисплея для каждой позиции символа на· экране имеется байт атрибутов, который хранит инфор­ мацию о цвете, подчеркивании и т.д. Байт статуса. Ячейка памяти, содержащая цепочку битов, описывающую текущий статус устройства. Бит четности. до·полнительн'?!Й (9-й) бит, добавл.яемый к каждому байту памяти, чтобы проверять возможные ошибки при передаче. Биты четности присоединяются также к данным при последовательной коммуникации. Битовая плоскость. В EGA видеобуфер разделен на четыре области, которые называются битовыми плоскостями 0-3. В режиме 16 цветов четыре плоскости параллельны, при этом 4 байта относятся к определенному адресу памяти (регистры задв.ижки определяют обмен данными между процессором и памятью дисплея). В некоторых случаях п~оскости могут быть связаны в цепь, образуя одну или две большие плос­ кости. Битовое поле. Когда байт или слово рассматриваются как цепочка битов, то некоторые биты, вз·ятые вместе, могут хранить определенный элемент информации. Например, биты 0-3 байта атрибутов символа на дисплее образуют битовое поле, которое определяет основной цвет символа. Битовые операции. Программные операции, читающие или изме­ няющие определенные биты данных. · 'Блок параметров. Группа переменных, создаваемая в памяти' для хранения информации, используемой устройством или функ­ цией операционной системы. Блок прямого доступа/. Блок записей, которые считываются или записЬIВаются за одну оnерацию с файлом прямого доступа при доступе к файлу методом управляющего блока файла. Блочные устройства. Устройства, которые посылают и принимают данные порциями в блок. Дисковые накопители являются наиболее распространенными блочными устройствами. Буфер. Область памяти, отводимая для хранения данных, которые будут передаваться от одной части компьютера к другой.
525 Буфер используется клавиатурой, то же самое относится и к дисковым накопителям и дисплею. Буфер клавиатуры. 15-символьная циклическая очередь, в кото­ рую прерывание клавиатуры помещает вводимые символы. Вектор. Четы,рехбайтовый адрес процедуры прерывания в памя,:и. -Старшие два байта дают сегмент, а младшие два байта - смещение. / Видеобуфер. Область памяти, отведенная для хранения инфор­ мации, выводимой на экран. Например, цветной графи­ ческий адаптер использует буфер размером l 6K. Микросхема управления дисплеt;м постоянно сканирует буфер, декодируя его содержимое и проектируя его на экран. Возврат. Выражение "при возврате ... " относится к~информации, которая будет содержаться в регистрах процессора после выполнения функции операционной системы. Вход. Слова "при входе" обычно относятся к установке регистров процессор~. которая должна быть сделана при выполнении функции операционной системы. Главная запись загрузки. Запись начальной загрузки на жестком диске. Она. содержит таблицу разделов, указывающую на различные разделы диска. Каждый из разделов содержит обычную запись начальной загрузки. которая инициирует загрузку соответствующей операционной системы. Глобальный символ. Один из символов '? или *, когда они используются в системе для указания неопределенных символов в именах файлов. Граница. Определенный интервал в r~амяти, в файле и т:д. Напри­ мер, программы размещаются в памяти, выравненными на 16-байтовую границу. Это означает, что &бсолютные адреса этих ячеек должны точно делиться на 'J 6,. Дерево каталогов. Система подкаталогов, организованная как ветви дерева, в которой на каталоги первого уровня имеются ссылки в корневом каталоге, а те в свою очередь содсржа:r ссылки на каталоги более низкого уровня. , , Дескриптор файла. В, Бейсике или другом языке высокого, уровня под дескриптором файла понимается номер буфера, с которым данный файл открывается,' т.е. как # 1 или #3.' Добавочный сегмент.. Область памяти, на которую указывает регистр процессора ES. Установка ES и DS (регистр сегмента
526 Глоссарий данных) часто используется совместно для переноса данных из одной части памяти в другую. Дорожка. Кольцо на диске. Дискета емкостью ЗбОК разделена на 40 дорожек, каждая из которых делится радиально на 9 секторов. Драйвер устройства. Про_граммная процедура, управляющая уст­ ройством, таким, как дисковый накопитель или принтер. Заголовок. Блок параметров, помещаемый в нач:~ло программы. драйвера устройства или другого массива кода или данных. Заголовок содерж·ит информацию о коде или данных, кото­ рая важна для их использования. Например, операционная система помещает 256-байтовый заголовок перед началом каждой загружаемой программы - префикс проrраммноrо сегмента - и использует содержащуюся в нем информацию для работы с этой программой. Заголовок запроса. Блок параметров, создаваемый системой для управления драйвером устройства. Заголовок устройства. Начальная часть процедуры драйвера уст­ ройства, которая идентифицирует устройство. Запись. Блок данных, указанного размера, являющийся единицей обмена данными с файлами. Запись начальной загрузки. Короткая программа, помещаемая на диск в такой позиции, которая считывается с диска в первую очередь при загрузке системы. Эта программа даст комп1,ю­ теру возможность загрузить части операционной системы. Запрос системы. Символы, появляющиеся в начале командной строки, например, А> или В>. Интерпретатор. Программа, которая переводит текст программы по одной инструкции, немедленно исполняя ее. Интерпрета­ торами являются программы BASIC.COM и BASICA.COM. Кластер. Группа дисковых секторов, образующая основную еди­ ницу, которая используется. при распределении дискового пространства. Код. Набор выполняемых инструкций, состав,1яющих программу, в отличие от данных, над которыми выпо,1няются опсращн1. Вообще говоря, кодом называется пос,1сдоватс.11,ност1, машинных инструкций, которые производит транслятор или ассемблер из текста программы.
Глоссарий 527 Код доступа. Этот термин используется в Техническом рука... водстве по MS-DOS для номера подфункции, т.е. для кода одной из нескольких функций, которые могут выполняться данным прерыванием. . Код завершения. Код, передаваемый процессом-потомком процессу-родителю. Например, когда одна программа запускает другую, то код завершения может б!>IТь передан от потомка родителю при завершении задачи потомка. Эти коды могут определяться программистом. . Код нажатия. Тип· скан-кода, который генерируется при нажатии клавиши (код освобождения выдается, • когда соответствующая клавиша отпускается). Код отпускания. Тип скан-кода, который генерируется при отпускании клавиши (код нажатия генерируется при нажатии клавиш}!). . Код ошибки. Кодовый номер, выдаваемый операционной системой для индикации определенною ошибочною условия. Код палитры. Номер, соответствующий определенному цвету из доступною набора. Код цвета. _Число от О до 15, которое относится к одному из шестнадцати цветов дисплея. Дисплей EGA, присоедюiенный :i<: улучшенному графическому адаптеру, может иметь 64 кода цвета (0-63). Кодовый сегмент. Область памяти, хранящая программный код (другие сегменты хранят данные и стек). Коды ASCII. Набор кодов от О до 127, соответствующих одному из 128 символов ASCII. IВМ РС использует расширенный набор кодов ASCII, состоящий из 256 символов. , Командная строка. Строка на экране дисплея, принимающая ' • · управляющую информацию, такая, как строка, начинающаяся с запроса операционной системы. Командный файл. •Фай.ц, содержащий список коы'анд и программ DOS, которые будут автоматически вызываться в том порядке, в котором они записаны, • либо порядок их выполнения может определя~ся условными_ операторами. Коммуникационное прерывание. Аппаратное прерывание, вызываемое адаптером асинхронной связи. Оно может происходить при получении очередною символа по линии .
528 Глоссарий связи, когда наступило время передавать следующий символ и т.п. Компилятор. Программа, преобразующая текст проrраммы на языке высокоrо уровня в файл,' содержащий исполняемый машинный код (или иногда в промежуточный код, который затем исполняется интерпретатором). Компоновщик. Программа, которая кочпонует вместе объектные • модули программы, организуя их адреса таким образом, чтобы модули могли взаимодействовать. Даже программы, состоящие из одноrо модуля, должны быть скомпонованы, поскольку компоновщик создает также код привязки. Конечная строка. Строка матрицы символов, на которой кончается изображение курсора. См. начальная строка. Корневой каталог. Центральный каталоr диска. Он расположен в • фиксированном месте на диске. Он может содержать список файлов, метку тома и указатели на подкаталоги. Критическая ошибка. Ошибка устройства, которiн1 делает дальнейшее выполнение программы невозможным. При этом вызывается обработчик критических ошибок операционной системы. Логический номер сектора. Вместо тоrо чтобы указывать сектора диска как "сторона х, до~жка х, сектор х", используются логические .номера секторов, которые определяют позицию сектора за счет последовател~,ной нумерации секторов, начиная с внешней rраницы диска. Маркировка. Термин, используемый для последовательноrо сигнала, когда он имеет высокий уровен1,, т.е. равен логической 1. В частности, rоворят, что сиrнал асинхронной связ1:1 маркирован в промежутки времени между передачей элементов данных. Маска. Цепочка битов, определяющая, какие из битов второй цепочки являются активными. Например, определенные аппаратные прерывания запрещаются за счет установки битов - в регистре маски микросхемы контроллера прерываний. При этом прерывание 4 маскируется цепочкой битов 00001000В. Массив ворот дисплея. Микросхема видеосистемы PCjr, содержащая ряд управляющих и статусных рсrистров, включая регистры палитры.
529 Масштабный коэффициент. Отношение числа точек, занимающих одно и то же расстояние по вертикали и горизонтали на экране терминала или печатающем устройстве. Машинная инструкция. Числовые коды, используемые процессором. Например, инструкция INТ кодируется как CD, а последовательность CD 21 приводит к тому, что Пр(ЧJ;е,ссор "3ЫПОЛНiJ,е, '1Г1Р;'J-,•сг;,ис 2lH. Машинный язык. Самый низкий уровень программирования, когда программист riишет инструкции непосредственно в двоичных кодах, исцользуемых процессором. Программирование на языке ассемблера приводит к тем же результ~;r:JМ.~.с большими удобствами за счет создания ДВОИЧНЫХ КОДОВ· из мнемоники типа MOV или TEST. Метка тома. Сnециальный элемент корневого обеспечивающий возможность идент11фициров&ть байтовым именем. каталога, ДИСК 11- Метод дескриптора файлов. Метод доступа к использованием номера файла. Этот метод файлам с практически вытеснил ранее используемый метод доступа с помощ1,ю управляющеrо блока файла. Метод управляющего блока файла. Набор_ функций операционной системы, позволяющий доступ к файлам посредетвом управляющеrо блока файла. Этот метод стал устаревшим после введения метода доступа дескрип;ора файлов. с использованием Микросхема поддержки. Одна из многих микросхем, которая связывает процессор с другими, частями компьютера или внешними устройствами. Наш словарь начинается со списка микросхем поддержки, обсуждаемых в данной книге. Мировые координаты. Система координат экрана, •опрсделяе~ая программой, устанавливающей диапазон значений координат х и у, которые могут включать или не включать отрицательные значения. Например, левому и правому краю экрана могут быть присвоены координаты -100 и 100. Эти координаты накладываются на систему физических координат экрана, в которой левый верхний ,угол всегда определяется значениями х = О и у = О и в которой используются только положительные значения. \
530 Глоссарий Начальная строка. Строка матрицы символов, на которой начинается изображение курсора. Например, для монохромного дисплея строка текста состоит из матрицы высотой в 14 строк, которые пронумерованы от О до 13. Для обычного курсора номер начальной строки 12, а конечной - 13. Начальный кластер. Первый кластер, с которого файл записывается на диск. Элемент каталога файлов указывает на начальный кластер, а таблица размещения файлов хранит информацию о последующих кластерах,· используемых файлом. Номер записи. Число, определяющее позицию записи в файле, отсчитываемое от О. В файле, содержащем записи длиной 1О байт, -запись номер 5 относится к 50-59. байтам файла, даже если записи с меньшими номерами не вводились. Номер записи прямого доступа. Номер, вводимый в поле записи прямого доступа управляющеГQ блока файла. Последующие файловые операции преобразуют этот номер в номер текущего блока и текущей записи. Номер параграфа. Номер, определяющий положение в памяти, основываясь на 16-байтов_ых единицах. Например, параграф номер 2 относится ко вторым 16 байтам памяти· и когда указатель указывает на этот параграф, то он указывает на 17-й байт памяти. Номер файла. Кодовый номер, возвращаемый системой, когда файл открывается с использованием метода дескрип:гора файлов. Этот номер впоследствии необходим для указания файла при дисковых операциях. Некоторые предопределенные номера идентифицируют дисплей, принтер и т.д. Область данных BIOS. Область данных, начинающаяся с адреса 0040:0000, в которой BIOS хранит статусную информацию и буфер клавиатуры. Область переноса данных. Буфер, используемый при доступе к файлам с помощью метода управляющего блока файла, который содержит. данные, _передаваемые на диск и;·и с диска.
531' • Обработка ошибок. Код, позволяющий программе rfd~tlbl управлеtfие специальной процедуре восстанов,1ення R-ltлyчac . возникновения критической ошибки при сбоях. Обработчик критических .ошибок. Прерывание системы, которое вызывается при.-возникновении критической ошибки. Можно заменить ero на сво~ ·процедуру восстановления при сбоях оборудования. , Обработчик прерывания. Процедvра прерывания. Этот термиtf ' более часто используется для· аппаратных прерываний. Обработчик прерывания устрой.ства. Основная часть процедуры драйвера устройства;· она содержит код, , ,выполняющий основные функции драйвера. f!')NoOP Объектный модуль. Файл, содержащий машинный код, в котором еще, не установлены относительные адреса. Компоновщик ~брабатьiвает и объединяет объектные модули, создавая исполняемые файлы типа ЕХЕ или СОМ·. 1 ()верлей. Подпрогр~мма, хранящаяся на диске до тех пор, пока она не потребуется головной программе. Она З~tгружается _в память поверх одной из частей вызывающей программы. Ограничитель. Специальный символ, разделяющий элементы данных. Операции в реальном времени. Программные операции, которые должны выполняться в определенный момент, когда кqмпьютер окажется способным их Мультипл~кация, сигналы тревоги и роботы работу ~ реальном времени. а не тогда, выполнить. используют Определение· перерыва. Способность адаптера коммуникации распознавать длинную последовательност1, логических нулей. ' Это сигнализирует о том, что отдаленная станция хочет перерыва в связи. Опрос. Управление периферийным устройством за счет постоянной проверки его статуса до тех пор, пока не произойдут желаемые изменения. Орнам~нт. Заполнение области графического дисплея определенной картинкой, а не одним цветом. Основной цвет. Цв~, которым символы или графические объекты выводятся на экран. От.ведение памяти. Отведение системой блока памяти для использования программой.
532 Глоссарий (i)lrнооит.ельный адрес. Адрес памяти, который указан в виде смещения относительно некоторой определенной точки памяти. Например, в СОМ-файлах переменные указываются адресами относительно начала программы. Относительные координаты. Координаты, определяемые относительно последних используемых координат. В этом случае 3,5 указывает "3 вправо и 5 вверх", а :-3,-5 - "3 влево и 5 вниз". Отображение в память. Помещение данных, выводимых на дисплей, непосредственно в видеобуфер • (откуда они проектируются на экран), вместо того чтобы использовать функции, предоставляемые операционной системой или языком высокого уровня. Ошибка обрамления. Ошибка при последовательной с,в~зи, когда поток данных несинхронизован, т.е. биты: данных, биты четности, стартовые и стоповые б_иты: не идут в правильной последовательности. Палитра. Набор цветов, доступных в определенном режиме дисплея. Параграф. 16-байтовая единица памяти, которая начин•ается на границе, делящейся на 16 без остатка. Параметр. Число, используемое для спецификации работы • устройства, функции операционной системы или оператора языка программирования. Переполнение. Переполнение происходит, когда данные в буфере или регистре стираются из-за поступления новых данных прежде, чем они были обработаны·. Подкаталог. Каталог, который ничем не отличается от корневого каталога, за исключением того, что он хранится на диске. как файл, а не в абсолютных секторах диска. Корневой каталог может содержать элементы, указывающие на подкаталоги, а они в ·свою очередь могут содержать элементы, описывающие другие подкаталоги. Подпрогр~мма на машинном языке. Подпрограмма, написанная на языке ассемблера, которая затем ассемблирована и вклю,ена в программу, написанную на языке высокого уровня. Такие , подпрограммы обычно создаются для ,операций, кqторые часто повторяются и должны выполняться очень быстро. В .зависимости от того, •
.5 33 испмьзуета ли транслятор или интерпретатор, маmиннке коды моrут быть скомпонованы с программой, вклlО'lе&ы в проrрамму отдельными строками или отдельно загружаться в память с двсха. Подтверидеиие. Сиrнал ввода/вывода, индицирующий, что задача выпОJIНена и оборудование roroвo начать выполнение новой задачи. П~дфунrщия. Одна из нескольких процедур, которые могут ВЬIПОJJВЯТЬСЯ данной функцией операциоJfной системы. В то время как номер фуи1щии всегда помещается в, АН, номер подфуикции •надо поместить в AL перед выполнени~м Iiрерываш. Поле. Группа битов или. байтов, отведенная для хранен~я определенною элемента данНЬiх. Порт. Путь, по которому происходит обмен данными между процессором и микросхемами поддержки. Порт А (порт В,. порт С). Один иs трех регистров, через которые программа получает доступ к микросхеме интерфейса с периферией 8255. • Прерывание. Прерывания - это программные процедуры, которые моrут ;вызываться двумя способами. Аппаратные прерывания инициируются оборудованием, напР,имер, когда нажимается клавиша на клавиатуре, то это событие мгновенно обрабатывается про~ессором, который выполняет требуемые дейсrвия и возвращается к прерванной работе. Программны~' прерываИИ51 служат для выполнения стандартных потребностей программиста, таких, как посылка символа на экран или принтер. Они предоставляются операционной системой и начинают работать, когда программа явно обратится к ним. Прерывание клавиатуры. Аппаратное прерывание, вызыв~емое, • когда IСJ1Звиmа на клавиатуре нажимается или оrпускается. Оно преобразует скан-коды, выдаваемые микропроцессором . 1. 1СJ1Эвватуры, . в. коды, используемые программами, и вставляет эти коды в буфер КJiавиатуры. • Прерывание принтера. Аппаратное прерывание, которое происходит, когда адаптер принтера посылает сигнал "fle занят... Процедура прерывания обычно· посылает· на принтер следуIОщий • байт выводимых данных и возвращает
534 Глоссарий управление. Таким образом можно выводить файлы на печать в то время, когда компьютер занят другой задачей. Прерывание таймера. Прерывание, инициируемое микросхемой таймера 8253 18,2 раза в секунду. Каждый раз это прерыван1,:е увеличивает счетчик времени суток BIOS. Префикс программного сегмента. 256-байтовый заголовок, который система помещает перед исполняемыми файлами при их загрузке в память. Он содержит переменные, используемые MS-DOS для управления программой, а также место для управляющего блока файла и область переноса данных. • Привязка. Процесс, выполняемый системой при загрузке программ типа ЕХЕ. Система вычисляет • базовые адреса (адреса сегментов), от которых будут отсчитываться все остальные адреса. Эти базовые адреса не могут быть установлены заранее, до . _загрузки программы, поскольку позиция программы в памяти до этого времени неизвестна. Программы типа СОМ не требуют привязки. Программное прерывание. Прерывание, вызываемое ин~рукцией INT. Пространство памяти. О(iласть адресуемой памяти, к которой процессор может иметь- доступ. Для микропроцессора 8088 адресуемое пространство равно приближенно одному миллиону байт. Протокол. Система параметров и форматов данных, используемых устройством. Процесс-потомок. Программа, запускаемая, когда другая программа (родитель) имеет управление. Процесс-родитель. ПрограмNiа, использующая другую программу (процесс-потомок). ' Прямое отображение в память. См. отображеиие в память. Прямой доступ к памяти. Способ осуществления очень быстрого обмена между периферийным устройством и памятью. Он особенно полезен при дисковых операциях. Этот метод использует специальную микросхему (которая отсутствует у PCjr). Раздел. Область жесткого диска. Жесткий диск может быть разбит на разделы, с тем чтобы он использовался несколькими операционными системами.
535 - Расширенный код. Код клавиши, используемый'{ для идентификации нажатия этой клавиши (или комбинации клавиш), для которой нет соответствующего симво.,Jа··;Iа наборе ASCII, такой, как функциональные клавиши •или :комбинации с клавишами «Ctrl~ или «Alt~ . Расширенные :КОДЬI имеют длину в два байта, причем первый байт всегда имеет значение ASCII О, чтобы отличить их от обычных кодов ASCII. Расширенный код ошибки. Начиная с версии 3.0 MS-DOS более подробные расширенные коды ошибки возвращаются при возникновении ошибки. Эти код~ сообщают не то,щ,ко об ошибке, но и об ее типе, ее месте в оборудо~!'lt1Нс:7М возможных способах восстановления. Расширенный управляющий блок файла. Управляющий блок файла, имеющий добавочное 7-байтовое поле заголовка, устанавливающее атрибуты файла. Регистр. Часть микросхемы, в которо~ данные хранятся и над ними производятся операции. В IВМ РС большинство реги~ров имеет размер 8 или. 16 бит. Регистры процессора получают значения из памяти и хранят их,_ пока они складываются, умножаются и т.д. Регистры микросхемь1 управления дисплеем ищщиализируются данными, определяющими характеристики дисплея. Регистр палитры. Один из 16 регистров EGA и РСjr,.указывающий цвет, который будет выво.циться на экран, когда соответствующий код_ цвета указан в видеобуфере. Регистр статуса. Регистр ввода/ вывода, содержащий· цепочку битов, описывающую текущий статус устройства. Регистры задвижки. У EGA имеется 4 однобайтовых регистра задвижки, которые хранят 4 байта данных, относящихся к определенному адресу видеобуфера. Когда процессо·р читает из буфера, то регистры задвижки заполняются, а когда процессор пишет в. видеобуфер, то .содержимое регистров задвижки пересылается в соответствующие ячейки памяти. Резидентная программа. Программа, остающаяся в памяти- после завершения. Система предохраняет ее от порчи другими загружаемыми программами, которые могут иметь достуr~ к _ содержащимся в данной программе процедурам через \, вектора прерывания.
536 Глоссарий рУiс~о~атие. Обмен предопределенными сигналами между двумя • ·\•стройствами для установления связи между ними. Связь в цепочку. У EGA видеопамять разделена на 4 битовые плоскости. Когда они объединяются в одну или две большие плоскости, то это называется связью в цепочку. • Сегмент. Область памяти размером 64К, созданная для хранения кода, данных или стека. Сегменты всегда выравнены .на границу 16 байт, поскольку их адрес получается ·умножением содержимого сегментноrо регистра на 16. Сегмент данных. Область памяти, содержащая данные программы. •В языке ассемблера на -лу область указывает регистр DS. Сегмент стека. Область памяти, отводимая для хранения стека. Сегмен,-ное значение. Число, определяющее положение в памяти в 16-байтовых единицах. То же, что и номер параграфа. Сегментный адрес. То же, что и сегментное значение или номер параграфа. Сегментный регистр. Один из четырех регистров процессора, указывающий на начальную позицию сегмента' памяти. Значение -лоrо регистра автоматически умножается на 16, с тем чтобы он указывал на одну из 16-байтовых границ мегабайтовоrо адресноrо пространства процессора. Иntена сегментных регистров CS (кодовый сегмент), DS (сегмент данных), SS (с-егмент стека) и ES (добавоч.ный сегмент). Символьное устройство. Устройство, которое посылает или принимает данные по одному символу, такие, как принтер. Сравните с блочными устройствами, которые обменива~ртся данными блоками. Синтаксический анализ. Разбиение текстовой строки на составляющие. части. MS-DOS может, проанализиров~в информацию в командной строке, переформатировать ее для использования функциями доступа к файлу. Синхронная связь. Последовательная связь, при кdторой приемная и передающая станции посылают и принимают сигналы со строrо синхронизованной скоростью. Системные часы. Кристалл, генерирующий импульсы заданной частоты, которая определяет работу всех устройств, в том числе и микросхемы таймера 8253.
537 Системный файл. Специальный статус, присваиваемы\оffii\~ посредством байта атрибутов. Он отмечает •файлl:.i, явлJIЮщиеся частью операционной системы. , · Скан-ход. Кодовое число, • посылаемое микропроцессором цавиатуры 8048 микросхеме интерфейса с периферией 8255 (или эквивале}Jтной), которое сообщает, какая клавиша клавиатуры была нажата или' отпущена: Прерывание клавиатуры преобразует с~ан-коды в коды АSСП или расширенные коды и устанавливает статус. IЧJавиш: переключателей. Скорость обме.-.а. Число битов в секунду, которое передае;rся при· обмене. - • - Скрытый файл. Статус, который может быть •присвоен файлу , усrанов~ой ero байта атрибутов. . Скрытые файлы не выводятся при выводе каталога файлов. Слово. Вообще rоворя, словом 'называется основная единица давньuс, и~ользуемая микропроцессором. В данной книге этот термин всегда относится к двухбайтовой величине.. Стартовый бит. При последовательной связи стартовый бит _предшествует каждому слову данных. Он состоит из нулевою . бита, отмечающеrо конец маркированного • ' \ состояния (серии единиц), которое заполняет все время в промежутках между передачей символов. Стек. Область памяти, используемая программой для временного хранения данных. 'последний элемент, помещае·мый в стек, •забирается оттуда l}ервым. Доступ к -стеку более быстрый, чем ж переменным. Стоповый бит. • При •последовательной связи сrоповые би,:ы. следуют за каждым словом данных. Они переводят коммуникационную линию в маркированное сосrояние и осrавляют ее в этом .состоянии на минимальное время, которое должно пройти, прежде чем можно щх:лать следующее слово. Страница. При работе с дисплеем страницей ,называется ·часть видеобуфера, хранящая данные для одного экрана. Можно переК.ЦIОЧ3ТЬ дисплей между-· страницами, .С тем чтобы ОН BЫIIOДIIJI сначала содержимое одной страницы, а затем друrой. Термин "сrраница•: часто используют и для/ обозвачеш 256-байтовоrо раздела памяти. / '
, 538 Глоссарий ~j')aтehtя устройства. Часть процедуры драйвера устройства, '> :· ,rtЯiязывающая драйвер с заголовком· запроса, который является блоком параметров, создаваемым системой для •управления драйвером. Строка ASCiiZ. То же, что и строка пути. Строка окружения. Строка, состоящая из одной или более , спецификаций, которым система следует при выполнении программы. Она может содержать конфигурационные команды, вводимые пользователем, такие, как BUFFERS или BREAK. ~юtiiпути. Строка, используемая для указания файла при 'S()J' дЬступе методом дескриптора файлов. Строка имеет тот же вид, что и при доступе на командном уровне системы. Она может начинатьсs~ с имени накопителя, может содержать имена подкаталогов, разделяемые обратной косой чертой,' и должна завершаться байтом ASCII О, отмечающим ее конец. Максимально допустимая длина строки 63 байта. Счетчик времени суток. Переменная в области данных BIOS, которая постоянно увеличивается прерыванием таймера. Ее значение используется операционной системой для вычисления времени суток. Счетчик команд. Регистр процессора, который указывает на программную инструкцию, которая будет выполняться следующей. Он отмечает смещение в кодовом сегменте. Таблица векторов. Таблица указателей. Вектора прерывания. содержатся в 256 четырехбайтовых полях, занимающих младшие 1024 байта памяти. Каждое поле содержит адрес процедуры прерывания. INT О указывается первым вектором, INT 1 - вторы:м и т.д. Таблица разделов. Таблица, содержащая главную запись загрузки на жестком диске. Она содержит информацию о размере и положении каждого раздела. Т;юлиt.tа размещения файлов. Таблица, имеющаяся на каждом диске, которая хранит информацию о •доступном дисковом • 1 пространстве и в которой записывается, какой кластер диска •какому файлу отведен. Тайм-аут; Выражение, используемое •при ,. операциях ,- ввода/вывода, у~азывающее, что периферийное устррйство не действует.
539 Текст программы. Исходный вариант программы, т.е.. вид программы до тоrо, как она была оттрансл~рована, ассемблирована или интерпретирована. • Текстовый файл ASCII. Последовательный текстовый ~й.1. в котором все числа представлены в виде символов ASCII, а ' • • элементы данных разделены парой возврат каретки/перевод строки и ·к~нец файла отмечен символом ·z (ASCII' 26). Текущий блок. Блок данных файла, состоящий. из ~ 28 записей,, на который fСЫЛаемся при доступе к файлу методом управляющеrо блока файла. См. текущий номер записи. Текущий каталог. Каталог, являющийся· частью дерева катщ~о~. к которому автоматически адресуются все файловые • . операции, до тех пор, .пока строка пути в сп~цификации файла не указывает дpyroro. Текущий номер записи. При доступе к файлам методом управляющего блока файла данные организованы в блоки по 128 запи.сей. Текущий номер записи - это номер записи в текущем блоке. Например, текущий н9мер • для записи прямоrо доступа номер 128 будет равен О, поскольку она' будет первой записью в блоке 1 (весь отсчет начинается с нуля, поэтому запись с номером 128 будет 129-й записltЮ файла, блок 1 - вторым блоком, а последняя запись блока О имеет номер 127). • Точка. Точка, выводимая в граф~ческом режиме. В·документации IBM ее называют также "pel". Указатель. Переменная, которая . содержит адрес другой переменной. Указатель накопителя. Дву:хбайтовая строка, именующая дисковый накопитель, в виде А:, В: и т.д. Управляющая строка. Строка символов, управляющая оборудованием. Управляющие, строки ·часто вкщочаются в поток' данных, посылаемых на принтер или модем. Они начинаются со специальноrо символа, указывающеrо их специальный статус (обычно символ ESC, ASCII 27). Управляющий блок памяти. 16-байтовый блок· параметров, создаваемый системой в начале каждого блока памяти,. отведенного программе с помощью функций расп~деления памяти.
540 Глоссарий 'упрамяющий блох файла. Блок параметров, создаваемый программой в памяти, для хранения информации, которая требуется системе для работы с файлом. Управляющий ход. Один из первых 32 символов набора ,кодов ASCII. • Они обычно используются для управления оборудованием, а не кодирования данных. Наиболее часто употребляемыми упра~ющими кодами являются возврат каретки и перевод строки. Устанавливаемые драйверы устройств. Драйвер устройства, .который ПОЛНОС1'ЬIО интегрирован с системой, что позволяет ему использовать специальные средства проверки ошибок и управляющие средства; Устройствц. Вообще· rоворя, устройством называется любое оборудование, которое хранит, выводит или обрабаты:вает информацию, такое, как диск<>вы:й накопите.ль, видеодисплей или принтер. Файловый указатель. Переменная, хранимая системой для каждого откры:тоrо файла. Файловый указатель отмечает позицию в файле, с которой будет выполняться следующая операция чтения или записи. Физические· координаты. .Координаты: точки на экране дисплея, отсчиты:ваемые от левоrо верхнего угла, который имеет координаты: 0,0. См. также мировые координат~ Флаг переноса. Один из битов регистра · флаrо1' процессора, который часто используется функциями MS-DOS для индmсации ошибки. . Флаги. Флаг - это переменная, -которая ио:жет бw либо включена, либо вы:к.лючена, сообщая о том, вы:полнено или нет определецное условие. Процессор имеет 16-битовы:й регистр флаrов, в котором отдельные биты служат для индикации раЗЛИЧНЬiх аспектов работы процессора. \ Фоновые операции. Втор~ процесс, осуществляемый параллельно вы:полнению программы. Например, текстовый редактор может пОСЫJiать данные на принтер, в то время как программа занята , редактированием. Фоновые операции могут работать за счет использования преры:ваний. Фоновый цвет. Фоновый цве:r используется диспл(:ем. Это тот цвет, который принимает. весь экран, когда он очищен.
541 Фунlщв8. В .113~ внсокоrо уровня функцией обычно ~ проц~ру,. хоторая преобразуе;r данные из одной фермы в друrу:ю. На уровне операционной L-Мстемы словом "функция" иазЬIВаIОТ любую из _процедур обработки прерывания. Точнее, определенное прерывание может выполнять несколько - процедур, каждая из которых наеывается функцией этоrо · прерывания (номер функции всегда • помещается в регистр АН при вызове прерывания). Сам11< фунщви могуr ~ержать ряд подфункций. Частота автоiювтора. Скорость, с ко:~-орой мавиша клавиатуры повториет посытсу кода, коrда она держится нажатой~ Цп.вичесuя очередь. Тип буфера данных, ,в котором-·-данные вставmосm:я с одноrо конца, а берутся с дpyroro. Текущие попож~ этих двух концов постоянно меняются и два· указателя хранят текущие положения "n>Jioвы" 11 "хвоста'". Циuический :контроль четности. Метод проверки ошибок, в к(!rором 33 nереданны_м блоком данны-х следует~·вычислснный математически . результат.; после приема вычисление повторя~я и сравнивается с переданным, чтобы быть уверенным, что данные переданы без искажения. Цилиндр. У ДИСКОВЫХ накопителей ЦIЩИНДроМ называется группа доРQ:жек, находящихся на одина:ковом расстоя1;1ии от центра диска ИJIИ дисков~ помещенных в накопитель. Эхо. Возврат для проверки. Например, при вводе с клавиатуры обычно выдается эхо на экран, выдается эхо и npft выводе через коммуникационный канал. Яэык ассемблера. Язык прог,раммирования самоrо 1:1изкоrо уровня, в котором программист пишет инструкции, непосредственно управляющие работой процессора.
ОГЛАВЛЕНИЕ Pt.' . Предисловие к русскому изданию 5 Введение ..................................................................................... 7 Глава 1. СИСТЕМНЫЕ РЕСУРСЫ ...... ................ ............ ...... 13 1.1. Ревизия системных ресурсов .................................................. 13 1.2. Управление прерываниями ..,.................................................. 31 1.3 . Управлейие программами .... ............................................ .... .. 42 Глава 2. ТАЙМЕРЫ И ЗВУК .................................................. 63 2.1 . Установка и чтение таймера .................................................. 63 2. 2 . Создание звука ....:.............................................................. 85 Глава 3. :КЛАВИАТУРА ........................................................... 110 3.1. У правление клавиатурой ....................................................... 11О 3.2. Доступ к отдельным клавишам ............................................... 137 3.3. Сводка кодов клавиш и их назначение ..................................... 151 Глава 4. ВЫВОД НА ТЕРМИНАЛ .......................................... 164 4.1. Управление выводом на терминал ......... ...... ....... ... ...... . . . ......... 164 4.2 . Упрамение курсором ........................................................... 192 4.3 . Вывод символов на экран ...................................................... , 205 4.4.·Вывод точечной графики ....................................................... 228 4.5. Сдвиг экрана и страницы .............. ... .. ....... .......... ....... ... ........ 270 Глава 5. ДИСКОВЫЕ НАКОПИТЕЛИ ................................... 283 5.1. Управление распределением диска .......................................... 283 • 5.2. Работа с каталогами диска ..................................................... 295 5.3. Подготовка к работе с файлами .............................................. 312 5.4 . Чтение и запись файла ........ .......... ... .... ................................ 336 Глава 6. ПРИНТЕР ...................................................'.............. 384 6.1 . Управление работой принтера ................................................ 384
/ 6.2. УстаноВIСА специфихаций_ печати ............................................ 6.3 . Посьuпса данных на, принтер .................................................. Глава 7. ВВОД/ВЫВОД ·······~··········"······················'"············· 7.1 . Доступ к посnедовательному пqрту ·······················-················ 7.2. Соsдание драйвера устройства ................•..: .....•...................... 7.3. Использование спеЦИ8JIЬНЬIХ усrройств ввода/вывода ········i""··... Прилож~ А. Двоичные и шестнадцатиричные числа и адресаЦИ11 П8М51ТИ •; •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• ПриложениеБ.БитовыеоперациивБейсиJСе ................., ............., ....... Приложение В. ОсноВНЪ1е !=IJeдeНИJI об !IЗЬПtе ассемблера ........................ Приложе,ше Г. ВJСJ1Ючение ассемблерных про-цедур в программы на Бейсике .....•.•...•.......•..................•....................•......•..........., ...• Приложение д. Использование драйвера устройства ANSI.SYS •...•.. ... .. ... . Приложение Е. Набор инсrрукций для мИJСропроцессора 8088 ................. Приложение Ж. Набор инсrрукций для микропроцессора 80286 .............. Глос:с:ариi .............., ........................:~ ...... ..................................... 543 395 411 432 432 460 478 491 496 500 501 509 510 515 519 ,1 / <. \.
Научное иэдание • Д.ордеiи Роберr СправоЧНJП: программиста персональных :w:омпьютеров типа mм РС, ХТ и АТ Кшаа одо6ре114 114 эасеоан,,ш сеlЩIШ редсовета по ~,апронноil oбpa{JomJCe данных• Э1UJН1.J,J1ШU 24.02.89 Зав. редакцией А.В. Рассказов , Редакторw Т.Т. Грюwсова, Е.В.1Сресты1нино118 Худ. редапор Ю.И. Артюхов Ред. изд. сисrемw Т.А. 1Соа11ова Техн. редактор Г.А. Попвова Корректоры Г.А. Jiашарина, Г.В. Хвоlщева, И.А. Поэrпе Переnдет художнюса Е.К. СамоАJюва Набор и репродуцируемый ориnuw~-макет иэrотомен МП "Гемма" ИБ N!2691 Подписано в печать 21.08 .91. Формат 60 Х 88 "1/16. Бум. офсетная. Гарнитура "Тайме". Печать офсетная. Усл. п. л. 33,32. Усл. кр.-отr. 33,32. Уч.-изц. л. 31,81. Тираж 100 ООО (2-й завод 50 001-100 ООО): Цена договорнu. Зак. 295. ' ' Иэдатеас:тво "Финанс:ы и ста~•, 101000, ул. ЧернJ,ввевскоrо, 7. ТИn. IIN, Кoт.llllКOII& ИQa'l'ellliC'l'8a •Финансы.и C'l'a1'IICТIIQ• NJIIIIICl'e(k:n нвформацнииnечuи CCQ- 19$273, ~.r+~lJ. , ·.