/
Author: Баазе С.
Tags: аналитическая химия программирование эвм языки программирования компьютерные технологии издательство финансы и статистика трансляторы
ISBN: 5—279—00062—0
Year: 1988
Text
СБаазе
Ассемблер
мини-ЭВМ
VAX-11
VAX-11
Assembly Language
Programming
Sara Baase
San Diego State University
PRENTICE-HALL, INC.,
Englewood Cliffs, New Jersey 07632
СБаазе
Ассемблер
мини-ЭВМ
VAX-11
Перевод с английского
В.Л. ГРИГОРЬЕВА
МОСКВА
’’ФИНАНСЫ И СТАТИСТИКА”
1988
ББК 24.4.1
Б12
2404010000—064
Б------------127—88
010(01)—88
ISBN 5—279—00062—0 (СССР)
ISBN 0—L3—940957—2 (США)
© 1983 by PRENTICE-HALL,
INC., Englewood Cliffs,
New Jersey 07632
© Перевод на русский язык,
предисловие, примечания,
«Финансы и статистика», 1988
Предисловие к русскому изданию
Специалистам в области вычислительной техники хорошо из-
вестна та огромная популярность, которую завоевало в 70-е годы
семейство ЭВМ PDPrll американской фирмы Digital Equipment
Corporation (DEC). Первоначально это семейство включало лишь
несколько моделей мини-ЭВМ, а затем к ним добавились мощная
миди-ЭВМ PDP-11/70, микроЭВМ LSI-11 и др. Широкое
распространение машин PDP-11 объясняется их несложной архи-
тектурой (например, основу центрального процессора образуют
всего восемь программно доступных 16-битовых регистров), разнооб-
разием режимов адресации памяти, тщательной проработкой интер-
фейсных шин UNIBUS и MASSBUS, развитой периферией.
Необходимо, также отметить обширное программное обеспечение
семейства PDP-11, объединяющее операционные системы, трансля-
торы почти всех известных языков программирования, средства
редактирования и отладки, множество пакетов прикладных про-
грамм и т. д. Немаловажное значение имеет и простота
использования таких машин. В нашей стране выпускаются несколь-
ко типов ЭВМ, программно совместимых с PDP-11, в частности
хорошо зарекомендовавшие себя машины СМ ЭВМ, микроЭВМ
типа <Электроника> и диалоговые вычислительные комплексы
(ДВК).
К концу 70-х годов технологические, системотехнические и
другие достижения в области вычислительной техники обусловили
переход на новый, более высокий архитектурный уровень мини-ЭВМ.
Фирма DEC разработала и выпустила систему VAX-11, представите-
ли которой были отнесены к классу супермини-ЭВМ и по своим
возможностям приблизились и даже превзошли большие ЭВМ
недавнего прошлого. Уже в самом названии VAX (Virtual Address
extension) отражены существенные архитектурные различия
между новым семейством и его предшественником (хотя номер
И сохранен для иллюстрации их преемственности). Система
VAX динамично развивается: за первой моделью VAX-11/780
последовали модели VAX-11/750, VAX-11/730 и двухпроцессорная
машина VAX-11/782. Не так давно появился мультипроцессорный
5
комплекс VAXcluster, а также ЭВМ MicroVAX-11. В системе
VAX-11 предусмотрен режим совместимости, в котором выпол-
няются программы для PDP-11. Однако, чтобы воспользоваться
преимуществами расширенной системы команд VAX-11, в программы
придется внести некоторые изменения.
Представление о возможностях семейства VAX-11 дает регистро-
вая модель центрального процессора, содержащая 16 32-битовых
регистров. Образно говоря, процессор PDP-11 расширен вдвое по
горизонтали и вертикали. Такое «аппаратное расширение» привело
к соответствующему «программному расширению» нижнего уров-
ня — допустимых типов данных и набора команд.
В архитектуре VAX-11 воплощено много интересных новшеств,
показывающих перспективные направления развития мини-ЭВМ.
Изучить их можно только с помощью языка Ассемблера, так
как языки программирования высокого уровня, по существу,
оказываются машинно-независимыми и неявно сопряжены с архитек-
турой ЭВМ. Язык Ассемблера позволяет не только разрабатывать
наиболее эффективные прикладные и системные программы и иметь
доступ ко всем ресурсам, но и «заглянуть» в машину на програм-
мном уровне.
Освоение новой ЭВМ—довольно трудоемкий и продолжитель-
ный процесс, и здесь не обойтись без хорошего пособия,
которое помогло бы постепенно разобраться в системе команд и осо-
бенностях программирования. Именно таким пособием и может стать
предлагаемая советскому читателю книга Сары Баазе «Ассемблер
мини-ЭВМ VAX-11». Конечно, эта книга не заменяет и не в состоя-
нии заменить объемную и подробную программную документацию
по машине, но позволяет существенно облегчить и ускорить ее
Изучение. Большое число примеров и упражнений способствует
усвоению материала, а также приобретению практических
навыков программирования. Книга удачно построена с методической
точки зрения: изложение ведется по принципу «от общего к частно-
му». Особой подготовки для чтения книги не требуется, но, как отме-
чено автором, весьма желательно знание одного из языков програм-
мирования высокого уровня.
Первые три главы кратко знакомят читателя с языком
Ассемблера, организацией памяти и данных, программной моделью
центрального процессора системы VAX-11 и кодированием целых чи-
сел в двоичной и шестнадцатеричной системах счисления. Особый
интерес представляют функции программно-доступных регистров
центрального процессора.
В гл. 4 речь идет о формате ассемблерных программ,
полях операторов, символических именах и метках, мнемониках
команд и простых режимах адресации. Поясняются директивы
резервирования и инициализации областей данных и системные
макрокоманды, начинающие и заканчивающие программу.'
6
В гл. 5 освещаются простые макрокоманды, позволяющие
осуществлять терминальный ввод-вывод без привлечения процедур
операционной системы. Эти макрокоманды реализуют ввод строки с
терминала, считывание записи из дискового файла, вывод на терми-
нал символьных строк и шестнадцатеричных чисел.
В гл. 6 рассматриваются команды, реализующие арифметичес-
кие операции, пересылки и преобразования данных, сдвиги и
некоторые другие операции над целыми двоичными числами.
Большинство операций можно выполнить одной командой для
каждого из трех основных типов целых чисел: байта (8 бит),
слова (16 бит) и длинного слова (32 бита). Кроме того, все
арифметические операции могут быть двух- или трехоперандными.
Здесь же описаны команды преобразований форматов и достаточно
универсальная команда управления циклом.
В гл. 7 приводятся команды передачи управления или, как
их еще называют в англоязычной литературе, команды управ-
ления программой. В VAX-11 предусмотрен обширный набор
команд условных и безусловных переходов, позволяющих проверить
все отношения между знаковыми и беззнаковыми числами,
условия переполнения и т. д. Каждая из команд управления
циклом инициирует такие действия, которые в других машинах
требуют привлечения нескольких команд.
В гл. 8, которую С. Баазе считает ключевой, показано,
как ЭВМ выполняет команды и как транслируются ассемблерные
операторы. Здесь довольно подробно описаны формат листинга
ассемблера и дополнительные режимы адресации, не вошедшие в
гл. 4, а также некоторые особые случаи, возникающие при
выполнении программы.
Следующие три главы посвящены процедурам (подпрограм-
мам), макрокомандам и организации программных секций.
Значительное внимание уделяется передаче параметров, использо-
ванию стека, связыванию ассемблерных процедур с программами
на языках высокого уровня, выражениям в поле операнда,
условному ассемблированию и т. д.
Гл. 12—14 завершают знакомство с системой команд VAX-11.
В них рассмотрены операции над отдельными разрядами, сдвиги
и манипуляции полями переменной длины, которые в других
машинах программируются длинными последовательностями команд.
Четыре формата чисел с плавающей точкой системы VAX-11
удовлетворяют принятому в США стандарту, а команды, иницииру-
ющие выполнение операций над такими числами, обеспечивают
реализацию сложных вычислительных алгоритмов. Наконец, в сис-
теме VAX-11 предусмотрены команды для выполнения арифме-
тических операций над десятичными числами, а также гибкие
команды обработки и редактирования символьных цепочек.
7
В заключительной главе рассматриваются основные положения
организации файловой системы и ввода-вывода под управлением
операционной системы.
Автор книги — преподаватель Калифорнийского университета,
что и определило ее построение в виде учебного пособия.
Однако книгой могут пользоваться не только студенты, специализи-
рующиеся в области программирования, но и профессиональные
программисты, а также инженерно-технические работники, имеющие
дело с вычислительной техникой и интересующиеся ее развитием.
Разумеется, изучение материала книги потребует от читателя
определенных затрат труда, поскольку смотреть «по диагонали» ее
бессмысленно. Наградой за этот труд будет освоение мощной и
своеобразной системы команд, различных типов данных и много-
численных режимов адресации памяти, приобретение практических
навыков программирования и умение в дальнейшем самостоятель-
но разрабатывать прикладные программы. Поскольку в нашей
стране налажен выпуск ЭВМ, программно совместимых с маши-
нами VAX-11, издание настоящей книги является весьма своевремен-
ным и, безусловно, окажет практическую помощь в освоении новой
техники.
Доктор технических
наук, профессор В. К. Злобин
Предисловие
Настоящая книга предлагается как учебное пособие по програм-
мированию на языке Ассемблера для курса CS3 «Введенйе
в вычислительную технику». Однако ею могут пользоваться все,
кто хочет изучить систему команд и язык Ассемблера системы
VAX-11.
Книга в основном ориентирована на читателей, которые еще
не сталкивались с программированием на языке Ассемблера.
Система VAX-11 используется как средство для иллюстрации
возможностей большинства современных ЭВМ и языков Ассемблера.
Читатели, знакомые с языком Ассемблера, также могут прочитать
эту книгу, опуская некоторые ее разделы. В конце каждой главы
приводятся краткие выводы и справочные таблицы по системе
VAX-11, которые будут полезны всем, независимо от уровня
подготовки (предполагается, что читатель знаком с каким-либо
языком высокого уровня).
Книга рассчитана на последовательное изучение материала с
тем, чтобы студенты могли закрепить полученные знания практикой
программирования простых задач. В связи с этим некоторые
темы (и команды) раскрываются неформально; подробное изло-
жение дается по мере необходимости. По той же причине главы, ка-
сающиеся собственно программирования, например о переходах
и процедурах, чередуются с главами, которые не связаны с
ним, например о машинных кодах и ассемблерных выражениях.
В книге нет строгих, логически обособленных разделов, как это
принято в руководствах фирм. Так, хотя команды управления
циклами рассматриваются в гл. 7, одна из них введена уже в гл. 6,
чтобы студенты смогли написать нетривиальную программу./
За один семестр мы успеваем пройти почти весь изложенный
в книге курс. При этом приходится пропустить описание некоторых
9
режимов адресации (гл. 8) и команд, оперирующих упакованными
десятичными числами (гл. 13), а также гл. 14 и некоторые
примеры. (Кроме того, ряд тем изучается не в полном
объеме).
Гл. 1 и 2, представляющие собой краткое введение, следует
освоить в первую очередь. Гл. 3, посвященную шестнадцатеричным
числам и дополнительному коду, можно пропустить, если этот
материал излагается в другом курсе.
В гл. 4 начинается знакомство с машинными командами и язы-
ком Ассемблера. В системе VAX предусмотрено много режимов
адресации операндов. Наиболее простые из них рассмотрены
в этой главе, а более сложные и менее часто применяемые —
в гл. 8.
Для того чтобы студенты могли осуществлять простой ввод-
вывод с терминала, в гл. 5 определены несколько несложных в обра-
щении макрокоманд ввода-вывода. (Библиотека VAX содержит
процедуры терминального ввода-вывода, но студентам проще пользо-
ваться макрокомандами.) В частности, определены макрокоманды
READLINE, READRCRD, PRINTCHRS и DUMPLONG. Здесь
приведены их функциональные характеристики и форматы аргу-
ментов, а описания макрокоманд даны в приложении D.
Изучив материал гл. 5, студенты должны уже уметь выполнять
простые программы, а после освоения гл. 6 — программы с циклами.
Хотя гл. 1—5 рассчитаны на аудиторные занятия, студенты
могут попробовать поработать с системой с разделением времени
и редактором в дисплейном классе. В первых шести главах
приводятся примеры программ, где используются. в основном сим-
вольные данные и вывод в шестнадцатеричном формате макро-
команды DUMPLONG, приемлемый для небольшого объема число-
вых данных. Более сложные преобразования между символьным
и дополнительным кодами (разд. 6.5) можно отложить до знакомства
с механизмом условных переходов (гл. 7).
В некотором смысле гл. 8 «Форматы машинного кода, тран-
сляция и выполнение» следует считать особо важной, хотя она и не
имеет непосредственного отношения к программированию. В этой
главе студенты, вероятно, впервые узнают, как ЭВМ реально
выполняет команды и как транслируются ассемблерные опера-
торы.
Поскольку гл. 8 предшествует глава о переходах и циклах,
ю
студенты могут писать нетривиальные программы уже в начале
курса. Однако они видят машинный код в листингах программ и
должны хотя бы немного разбираться в форматах команд и особен-
ностях их выполнения, чтобы понимать и исправлять ошибки,
поэтому целесообразно рассмотреть часть материала гл. 8 (разд,
8.8, в котором поясняются некоторые сообщения об ошибках
времени выполнения) до завершения гл. 7. Именно так мы и поступа-
ем, а разбивать гл. 7 и 8 на меньшие части нам представляется
неудобным.
В гл. 9 обсуждаются проблемы взаимодействия процедур
и вызывающих их программ. Проанализированы несколько
способов решения этих проблем, но основной акцент сделан
на стандартный для VAX механизм вызова процедур. В главу
введен небольшой раздел о соглашениях VAX в отношении связи
языка Ассемблера с другими языками. Информации, содержащейся
в нем, вполне достаточно для выполнения простых задач, однако
приведены не все типы аргументов.
В гл. 10 освещаются тонкости работы ассемблера (и редактора
связей): здесь описываются выражения и различия между абсолют-
ными и переместимыми выражениями.
Макросредства VAX не являются особенно мощными или эле-
гантными, но они предоставляют ряд стандартных возможностей,
например конкатенацию аргументов, локальные метки и множество
директив условного ассемблирования. Некоторые из этих возможнос-
тей, а также общие вопросы применения макрокоманд рассмат-
риваются в гл. 11.
В гл. 12, где описываются битовые и логические команды,
включен раздел о двоичных полях переменной длины и соответ-
ствующих командах.
Гл. 13 посвящена командам, которые позволяют манипулировать
числами с плавающей точкой и упакованными десятичными числами.
Рассмотрены вопросы точности вычислений.
В гл. 14 обсуждаются команды, с помощью которых можно
оперировать символьными цепочками, в том числе команды поиска,
преобразования и редактирования.
Краткое введение в службу управления записями VAX-11
содержится в гл. 15. Эта служба дает возможность реализовать
ряд операций ввода-вывода без привлечения макрокоманд, описан-
ных в гл. 5. Более подробное изложение операций и устройств
11
ввода-вывода связано с рассмотрением обширного материала по
операционным системам, который в настоящую книгу не включен.
В книге описываются не вся система команд VAX-11
и не все .директивы ассемблера. Несколько команд (например,
арифметические с тетрасловами) используются только в упражне-
ниях. В системе VAX-11 предусмотрены мощные и необычные
команды, предназначенные для эффективной реализации некоторых
конструкций языков высокого уровня (например, оператора CASE).
Здесь они не рассматриваются, но приведены в приложении А,
так что при необходимости студенты смогут получить нужную
информацию из справочных руководств, основными из которых
являются
VAX Architecture Handbook и VAX-11 Macro Language Reference
Manual.
Определенную помощь могут оказать и следующие руководства:
VAX-11 Macro User’s Guide, VAX-11 Command Language User’s
Guide, VAX-11 Symbolic Debugger Reference Manual, VAX-11
Record Management Services Reference Manual.
В книге приведено несколько сотен упражнений. Одни из них
требуют кратких решений, другие посвящены серьезным проблемам
программирования. Ответы на некоторые упражнения даны в при-
ложении Е.
В заключение мне хотелось бы выразить признательность моему
коллеге Р. Хагеру за поддержку идеи написания этой книги,
студентам, обнаружившим ошибки в рукописи, когда она использо-
валась как учебное пособие, преподавателям Т. Тигардену и
Д. ван Зандту, апробировавшим будущее учебное пособие в своих
группах, и Дж. Ревелли за написание, редактирование и форматиро-
вание программ, а также за ряд полезных советов при подготов-
ке книги. Я благодарю также своих коллег по факультету
вычислительной техники университета в Сан-Диего, которые оказа-
ли мне хотя и косвенную, но неоценимую помощь.
С. Баазе
Глава 1
Введение
1.1. Что такое язык Ассемблера
и почему необходимо его изучать?
Язык Ассемблера — это язык программирования, в котором
команды инициируют отдельные простейшие операции, выполняемые
конкретной ЭВМ. Эти команды, закодированные так, что они могут
непосредственно воздействовать на схемы ЭВМ, образуют машинный
язык1. Разумеется, приведенные определения не являются формально
строгими, но вместе с последующими пояснениями и примерами
они все же дают читателю общее представление о том, как
«выглядит» язык Ассемблера, какие команды допустимы в нем й
чем он отличается от языков высокого уровня и машинного
языка. Настоящая книга' посвящена языку Ассемблера системы
VAX-11, выпускаемой фирмой DEC. Каждый приводимый здесь при-
мер содержит оператор языка высокого уровня и демонстрирует
возможную трансляцию его на язык Ассемблера VAX-11 (назы-
ваемый VAX-11 MACRO) и машинный язык.
Первое и наиболее существенное различие трех упомянутых
выше видов языков программирования заключается в том, что по
мере перехода от языков высокого уровня к языкам более низ-
ких уровней становится все труднее разбираться в программе.
Основные достоинства языков высокого уровня — машинная неза-
висимость и «понятность» написанных на них программ. Команды
записываются с привлечением английского языка и привычной
математической символики, а программы могут выполняться (с
небольшими изменениями) на различных ЭВМ. В каждой ЭВМ
имеется свой компилятор для трансляции программ, написанных
на языке высокого уровня, на ее машинный язык.
Некоторые фрагменты ассемблерных команд приходится расшиф-
1 Машинный язык (т. е. язык «нулей и единиц») почти эквивалентен языку
Ассемблера в том смысле, что одна ассемблерная команда соответствует одной
машинной команде.—Примеч. пер.
13
ровывать: в примере 1.1 указываются имена переменных и остается
только предполагать, что предписывает каждая команда. Очевидно,
по команде MULF2 осуществляется умножение, а по команде
ADDF3 — сложение. Машинный код абсолютно не понятен без
дополнительных пояснений и даже знание всех правил тран-
сляции не превращает чтение и запись команд на машинном
языке в простое и приятное занятие.
Пример. 1.1.
ФОРТРАН
COST - BASE* NUM » VAR
ЯЗЫК АССЕМБЛЕРА МАШИННЫЙ КОД
COSTi .BLKF 1
BASE! .BLKF 1
VARi .BLKF 1
NUM! .BLKL 1
CVTLF NUM,R3 53DBAF4E
MULF2 VAR,R3 53D3AF44
ADDF3 BA8E,R3,C0ST C4AF53CBAF41
Пример 1.2.
ПАСКАЛЬ
IF DIR < 0 THEN SUM i- SUM * AMNTCI3
ЯЗЫК АССЕМБЛЕРА МАШИННЫЙ КОД
TSTB (R2) 6295
BBEQ NEXT 0418
ADDL2 (R4)CR53,R6 566445C0
NEXTi <следующая команда>
Второе очевидное различие языков состоит в том, что для коди-
рования одной строки программы на языке высокого уровня
требуются несколько строк ассемблерной программы. Ранние ЭВМ
могли выполнять весьма ограниченное число команд. Их системы
команд включали в себя такие операции, как целочисленные
сложение и вычитание, проверка знака, условный переход, некото-
рые логические операции, ввод, вывод и пересылка данных.
14
Каждая команда предполагала выполнение одной операции.
(Такие же ограниченные системы команд имеют многие современ-
ные микропроцессоры.) Целочисленные умножение и деление и
арифметические операции над числами с плавающей точкой прихо-
дилось программировать, привлекая более простые операции.
В современных же больших ЭВМ есть машинные команды,
которые обеспечивают выполнение целочисленных умножения и
деления, арифметических операций над числами и плавающей
точкой, а также множества более сложных логических операций
и операций преобразований данных.
Для выполнения оператора Фортрана в примере 1.1 требуются
несколько операций: преобразование формата целого числа NUM в
формат с плавающей точкой, умножение, сложение и присваивание
результата переменной COST. Во многих ЭВМ преобразование
связано с выполнением длинной последовательности команд, а
каждая из остальных операций выполняется по отдельной
команде. Однако VAX-11 обладает очень мощной системой команд;
преобразование осуществляется с помощью одной команды CVTLF,
а по команде сложения производится не только суммирование,
но и запоминание результата. В примере 1.2 оператор языка
Паскаль преобразуется в команды проверки знака (TSTB),
условного перехода (BGEQ—перейти, если больше или равно)
и сложения. В других ЭВМ для обращения к массиву AMNT
может понадобиться большее число команд, но VAX имеет весьма
эффективные и гибкие средства доступа к данным.
Машинный код некоторого программного фрагмента в памяти
ЭВМ представлен непрерывной последовательностью кодов. В каж-
дом примере он разделен на отдельные строки, что позволяет
показать соответствие секций машинного кода и ассемблерных
команд.
В действительности машинный код несколько «хуже» приведенно-
го: он состоит из длинных цепочек битов — нулей и единиц.
В примерах использована сокращенная запись, в которой каждые
цифра и буква образуют группу из четырех битов, что
соответствует шестнадцатеричной системе счисления. Такая система
широко применяется для записи машинного кода, адресов памяти и
данных (подробнее см. гл. 3).
Приведенные в примерах ассемблерные и машинные последо-
вательности кодов оказываются не единственными реализациями
показанных операторов Фортрана и Паскаля. Они зависят от
некоторых предположений о контексте операторов. Так, в примере
1.1 первые четыре строки распределяют память для переменных, а в
примере 1.2 аналогичные строки отсутствуют. В языке Ассемблера
имена всех переменных должны быть определены; грубо говоря, им
должно быть выделено место в памяти. Во многих случаях
допускается обращение к переменным по именам, как это принято
15
в языках высокого уровня и сделано в примере 1.1. В примере
1.2 предполагается, что оператор нахрдится в подпрограмме (или
процедуре), a DIR, SUM и AMNT являются /аргументами.
Для данных, которыми оперирует подпрограмма, где-то в памяти
должно быть выделено место, но имена переменных (если они
есть) здесь использовать нельзя. Более того, нельзя даже
использовать имена формальных (фиктивных) аргументов по ана-
логии с процедурами или подпрограммами, написанными на языках
высокого уровня.
Машинный код не содержит определений (объявлений) имен
переменных и меток операторов (как NEXT в примере 1.2).
В него включены только исполняемые команды. Обращения к данным
и командам закодированы с помощью методов, рассматриваемых
в гл. 8. •
В ситуациях, когда программировать на языке высокого
уровня невозможно, язык Ассемблера, конечно, предпочтительнее
машинного. Кроме очевидного достоинства — более наглядной запи-
си, язык Ассемблера имеет целый ряд преимуществ перед машинным
кодом. Употребление символических имен для данных и меток
команд освобождает программиста от вычислений адресов ячеек
памяти при внесении в программу изменений. Далее, с помощью
макрокоманд, которые обычно предусматриваются в языке Ассем-
блера, программист может не повторять аналогичные секции
кода, встречающиеся в нескольких местах программы. Ассемблеры
выполняют для пользователя множество служебных функций.
Очень часто компиляторы осуществляют трансляцию на язык
Ассемблера, а не в машинный код.
Почему же следует отдавать предпочтение языку Ассемблера,
имея возможность выбора между ним и языком высокого уровня?
Ведь тот факт, что объем программирования на языке Ассем-
блера больше, чем при использовании языков высокого уровня,
говорит не в пользу выбора Ассемблера. Однако в ряде ситуаций
язык высокого уровня неудобен или даже неприемлем. Например,
первый компилятор нельзя написать на том языке высокого
уровня, с которого он осуществляет трансляцию, так как его
невозможно реализовать. (Тем не менее сейчас компиляторы
все-таки разрабатываются на языках высокого уровня.)
Программы для управления и взаимодействия с периферийными
устройствами (устройствами ввода-вывода) обычно пишутся на язы-
ке Ассемблера, так как в них применяются команды, недоступные
в языках высокого уровня, и, кроме того, эти программы должны
быть весьма эффективными. По аналогичным причинам некото-
рые программные системы написаны на языке Ассемблера.
Следует учитывать, что языки высокого уровня разрабатываются
без анализа особенностей какой-либо конкретной ЭВМ, а компи-
лятор должен стандартным образом транслировать любую
допустимую программу. Поэтому в тех случаях, когда необходимо
16
опираться на специальные средства ЭВМ, программировать
такие операции, которые недопустимы в языке высокого уровня,
или повысить эффективность* программы, целесообразно остано-
виться на языке Ассемблера.
Хотя ассемблерные программы приходится транслировать в
машинный код, их трансляция оказывается проще по сравнению
с программами на языках высокого уровня, поскольку язык
Ассемблера близок к машинному языку для любой конкретной
ЭВМ. Программа, осуществляющая трансляцию с языка Ассем-
блера на Машинный язык, называется ассемблером.
Существует несколько важных аргументов в пользу изучения
языка Ассемблера, связанных с его практическим применением
как языка программирования. Напомним, что Фортран был создан
в 50-х годах, когда ЭВМ строились на вакуумных лампах.
Та же самая .программа, написанная на языке Фортран,
которая выполнялась на ламповых ЭВМ, может быть выполнена
на транзисторных ЭВМ 60-х годов, на современных ЭВМ с элемент-
ной базой из интегральных микросхем и на ЭВМ будущего,
разработанной по какой-то новой технологии. При знакомстве с
Фортраном (и другими языками высокого уровня) мы не получаем
почти никаких сведений об устройстве и принципах работы ЭВМ.
Не касаясь вопроса об аппаратных средствах ЭВМ, отметим лишь,
что за прошедшие годы произошли кардинальные изменения,
в большинстве своем невидимые программистам, работающим на
языках высокого уровня, в том числе и в архитектуре ЭВМ,
т. е. в ее общей структуре и функциональных характеристиках.
Поэтому главная цель изучения языка Ассемблера — разобраться
в архитектуре ЭВМ (кстати, фирма DEC дает определение архитек-
туры как «атрибутов системы, видимых программистам, работаю-
щим на языке Ассемблера»). Таким образом, кроме собственно
программирования на языке Ассемблера, мы рассмотрим здесь
структуру ЭВМ, систему команд, дешифрирование и выполнение
команд, способы представления данных, методы обращения к ячей-
кам памяти и передачу параметров подпрограммам (процедурам).
Это поможет вам понять, как функционирует ЭВМ и как с по-
мощью компиляторов должны транслироваться программы, написан-
ные на языке высокого уровня.
Архитектура ЭВМ и языки Ассемблера за последние 30 лет
существенно изменились. Несмотря на то что ЭВМ разных
фирм имеют значительные различия, они предоставляют много ана-
логичных возможностей и обладают целым рядом подобных
характеристик. Так чем же объяснить* наш выбор Ассемблера
системы VAX? Обычно автор описывает язык Ассемблера той ЭВМ,
1С применением оптимизирующих компиляторов эта причина исключается,
поскольку их эффективность приближается к эффективности работы опытных
программистов на языке Ассемблера.
17
на которой он работает, а не из-за каких-либо особых достоинств
языка Ассемблера этой ЭВМ. Если программист намерева-
ется использовать систему VAX и писать программы на языке
Ассемблера, очевидно, он выберет VAX-11 MACRO*. Но если кто-
то захочет изучить архитектуру современной ЭВМ, в зависимости
от конкретных целей можно остановиться на нескольких ЭВМ. Систе-
ма VAX представляется нам удачным вариантом, поскольку, как по-
казано в приводимых ниже примерах, она имеет такие команды и ме-
тоды обращения к данным, которые оказываются более мощными
и гибкими, чем в других ЭВМ. Вместе с тем многие черты VAX и ее
языка Ассемблера типичны для существующих ЭВМ. Кроме того,
в системе VAX воплощена прекрасная архитектура. В дальнейшем
мы будем проводить аналогии и обсуждать различия между VAX
и другими ЭВМ.
1.2. Терминология
Ниже приводятся и кратко поясняются наиболее распространен-
ные термины, многие из которых, по-видимому, знакомы чита-
телю.
Данные. Представляют собой «кусочки» некоторой информации
в форме чисел или символьных цепочек. Для обозначения дан-
ных в единственном числе применяется термин данное.
Бит. Обычно определяется как двоичная цифра: 0 или 1. Но бит
может обозначать и элементарную ячейку (например, в машинной
памяти), в которой хранится 0 или 1. Во многих ЭВМ,
включая и систему VAX, восемь бит объединяются в группу,
называемую байтом. В таких ЭВМ байт считается основной
единицей памяти. Половина байта часто называется тетрадой
(nibble)1 2. Инвертировать (или дополнить) бит — значит изменить
его значение, т. е. изменить 0 на 1 или 1 на 0. Инверсия
двоичной цепочки подразумевает инвертирование каждого ее бита.
Компилятор'. Программа, транслирующая язык высокого уровня
на язык Ассемблера. При трансляции компиляторы обычно
формируют листинг программы и сообщений о синтаксических
ошибках в транслируемой программе.
Ассемблер. Программа, транслирующая язык Ассемблера на
машинный язый.! Написанная программистом программа называется
исходной (синонимы — исходный файл, исходный модуль). Основной
результат работы компилятора или ассемблера называется объект-
ным модулем (или объектным файлом). Как и компилятор, ассемблер.
1 Ассемблерные программы PDP-11 могут выполняться на VAX в так называемом
режиме совместимости, но здесь этот режим не рассматривается.
2 Термин nibble (буквально — «огрызок») свидетельствует о том, что специалисты
по ЭВМ не лишены чувства юмора.
18
формирует сообщения об ошибках и листинг программы. Листинг
показывает машинный код, построенный ассемблером.
Поскольку программа может состоять из нескольких ассембли-
руемых по отдельности модулей и процедур, а ассемблер
«не знает», в какой области памяти она будет находиться при
выполнении, объектный файл не является окончательным машинным
кодом. Нужна еще одна программа.
Редактор связей (компоновщик). Программа, объединяющая
различные объектные модули для приведения их к исполняемой
форме. Выход редактора связей называется исполняемым образом
или исполняемым файлом.
Исполняемая команда. Команда, которая транслируется в одну
или несколько машинных команд. Другие находящиеся в программе
указания, например операторы объявлений и заголовка, содержат
информацию или инструкции для ассемблера или компилятора.
Как уже показано выше, программа проходит три этапа:
ассемблирование, редактирование связей и выполнение. При изу-
чении языка Ассемблера и программировании на нем важно пони-
мать, какие операции реализуются на каждом из этих этапов.
Поэтому мы будем говорить об операции или ошибке, имеющей
место во время ассемблирования или во время выполнения. Время
ассемблирования — это не продолжительность трансляции програм-
мы, а временной интервал или этап, когда осуществляется ассем-
блирование. Аналогично следует трактовать и термин «время выпол-
нения».
Процедура. Программная секция, которая выполняет конкрет-
ные действия над аргументами, передаваемыми ей при вызове
из другой программы. Процедуру допускается ассемблировать (или
компилировать) как независимую единицу. (В языке Фортран про-
цедуры называются подпрограммами.)
Мы уже несколько раз использовали термин «модуль». Он
встречается довольно часто, но дать его точное определение
затруднительно.
Модуль. Программная секция — это абстрактная категория
(в конкретном представлении это может быть, например, исходный
или объектный файл), которая рассматривается как нечто целое
в контексте той или иной задачи. Этим термином мы будем
обозначать секцию ассемблерного исходного кода, ассемблируемую
как единица. В общем случае модулем может называться основ-
ная программа или процедура.
Мультипрограммирование. Одновременное выполнение несколь-
ких программ. Центральный процессор CPU в любой момент
времени выполняет только одну команду, но его быстродействие
настолько велико, что во избежание недоиспользования этого
ценного ресурса в памяти одновременно размещают несколько
программ, и пока одна из них ожидает завершения относи-
тельно медленной операции (длящейся, например, десятки
19
миллисекунд), может выполняться другая программа. Примером
медленной операции служит считывание данных с диска или действи-
тельно медленный процесс — пользователь за терминалом обду-
мывает свое следующее действие.
Операционная система. Набор программ, которые распределяют
и управляют ресурсами вычислительной системы. Она планирует
все работы в системе, управляет вводом и выводом, осущест-
вляет манипуляции записями и файлами. Стандартная операцион-
ная система VAX-11 называется VAX/VMS (Virtual Address
extensions/Virtual Memory System — расширение виртуального ад-
реса/система виртуальной памяти).
V
Глава 2
Машинная организация
Вычислительная система состоит из трех подсистем, показанных
на рис. 2.1: памяти, центрального процессора и подсистемы
ввода-вывода. В настоящей главе рассматривается архитектура
(логическая структура) вычислительной системы.
Центральный процессор (CPU)
Общие регистры
PSL и другие
специальные регистры
Арифметические
и логические устройства
Память
Ввод-вывод
Диски
Терминалы
Принтеры
Рис. 2.1. Подсистемы ЭВМ. Стрелками показаны направ-
ления обмена информацией между тремя подсистемами
2.1. Организация памяти и данных
Физическая' память. Физической или основной памятью ЭВМ
называется память, где хранятся команды и данные, которые
процессор может считывать и выполнять или., дбрабатывать.
В системе VAX применяется полупроводниковая МОП-память,
построенная на микросхемах, содержащих тысячи миниатюрных
электрических Схем. Каждая из таких схем в любой момент
времени может быть включена или выключена. Одно из этих
состояний представляется как 0, а другое — как 1. В ранних
ЭВМ память строилась на магнитных кольцах, называемых сердеч-
никами. Сердечник можно было намагнитить в одном из двух
направлений и таким образом представить один бит. В системе
VAX (и многих других ЭВМ) группа из 8 бит образует
единицу памяти — байт.
В машине VAX-11/780 передача операнда из памяти в централь-
ный процессор длится около 300 нс, а в VAX-11/750 — около
400 нс (1 нс=10“9 с). Для достижения столь малого времени
обращения в обеих машинах применяется специальная быстро-
действующая память, называемая кэш-памятыо.
Объем основной памяти VAX-11/780 составляет до 8 Мбайт
(в первом приближении — 8 млн. байт), a VAX-11/750 — до
2 Мбайт.
Логическая структура памяти. В общем память представляет
собой последовательность байтов, пронумерованных от 0 до некото-
рого максимума, допустимого в конкретной установке. Номер
байта называется его адресом.
Биты в байте нумеруются справа налево, начиная с 0:
правый (младший) бит имеет номер 0, а левый (старший) бит —
номер 7. Номер бита есть показатель степени 2 для этого бита,
когда данные интерпретируются как двоичные числа.
Поскольку каждый бит может иметь одно из двух значений (0 или
1), байт содержит 256 (28) комбинаций. Байт можно использовать
для хранения символа или целого числа с небольшим значением.
(Символы кодируются семью битами, а левый, восьмой, бит всегда
нулевой.)
Байты оказываются слишком малыми единицами памяти для
хранения больших целых чисел или чисел с плавающей точкой.
Поэтому допускается группирование байтов. Существует несколько
способов группирования. Каждая такая группа в памяти имеет
свой адрес: им является адрес первого байта, т. е. байта с
наименьшим номером.
Слово состоит из двух смежных (соседних) байтов, или 16 бит.
Биты в слове нумеруются от 0 до 15, начиная с правого (млад-
шего) бита. Слова применяются для хранения знаковых целых чисел
в диапазоне от —32768 до +32767 и беззнаковых целых чисел в диа-
пазоне от 0 до 65535. Когда слово, или одна из описываемых ниже
многобайтных единиц памяти, используется для представления
целого числа, младшие биты находятся в первом байте (с наимень-
шим адресом). Для представления числа в естественной форме
со старшими разрядами слева необходимо разместить байты памяти
справа налево, как показано на рис. 2.2. Такое представление может
привести к путанице, поэтому будьте внимательны! (Чтобы
как-то «скомпенсировать» это неудобство, при печати листингов ма-
шинного кода и дампов содержимое памяти выводится в строке спра-
ва налево. В этом случае целые числа читаются легко, а данные,
хранящиеся в естественном порядке, оказываются «перевернутыми».
22
A+l______________________A_____________ Адреса
0 0 0 0 0 0 1 0 1 1 1 1 1 1 1 0 0 10 1110 1 1 1 1 1 1
15 8 7 0 Номера битов
Рис. 2.2. Целое число в многобайтовой единице памяти. Стар-
шие биты многобайтовых данных находятся в байтах с боль-
шими адресами. Поэтому на диаграммах памяти байты
нумеруются справа налево. На этом рисунке слово начинает-
ся в байте с номером А. Оно содержит двдичное представле-
ние числа 558
Для работы с таким представлением данных потребуется некоторый
опыт.)
Длинное слово состоит из четырех смежных байтов, или 32 бит.
Биты нумеруются справа налево от 0 до 31. Длинные
слова могут хранить очень большие знаковые или беззнаковые це-
лые числа. Их можно также использовать для хранения чисел
с плавающей точкой, имеющих ординарную точность. Счетверен-
ное слово' (или тетраслово) состоит из 8 смежных байт, или
64 бит, нумеруемых справа налево от 0 до 63. Тетраслова применя-
ются для хранения очень больших целых чисел или чисел с плаваю-
щей точкой двойной точности. Свосьмеренное слово' (или октаслово)
состоит из 16 смежных байт, биты которых нумеруются от
О до 127. Система VAX имеет полный набор команд целочислен-
ной арифметйки для байтов, слов и длинных слов, но пока
не располагает такими командами для тетраслов и октаслов.
На рис. 2.3 показаны рассмотренные выше форматы, иллюстри-
рующие нумерацию битов и адресацию этих единиц памяти.
Для других типов данных байты можно группировать по-иному.
Например, один байт может хранить код одного символа, а смежная
последовательность байтов — символьную цепочку. В последнем
случае число используемых байтов зависит от длины символьной
цепочки.
Слово, длинное слово или тетраслово могут начинаться с любо-
го байта в памяти, но обработка их оказывается более эффективной,
если они выравнены так, будто вся память равномерно разде-
лена на элементы одного и того же размера. Иначе говоря,
слово всегда должно начинаться с байта, адрес которого кратен
двум, длинное слово — с байта, адрес которого кратен* четырем, а
тетраслово — с байта, имеющего адрес, кратный восьми.
В вычислительных системах применяются слова разной длины
(чаще всего в диапазоне от 16 до 60 бит). Наиболее рас-
пространены слова длиной в 16 и 32 бита. Иногда основной
единицей памяти является слово, а не байт, и адреса имеют
только слова.
1 Далее в тексте используются более удобные, по мнению переводчика,
термины <тетраслово» (quadword) и «октаслово» (octaword).—Примеч. пер.
23
А Адрес
Байт
7 0 Биты
А+1 А Адреса
Слово
15 87 0 Биты
А+3 А+2 А+1 А Адреса
Длинное ||||
слово | II
31 24 23 16 15 8 7 0 Биты
А+7 А+1 А Адреса
Тетраслово | * * *" |
63 56 15 8 7 0 Биты
А+15 А+1 А Адреса
Октаслово | * * * | 1
127 120 15 8 7 0 Биты
Рис. 2.3. Единицы памяти
Для хранения очень малых чисел или набора чисел, которые
не требуют целого числа байтов, неэффективно использовать память,
считая основной ее единицей байт. Программист, работающий на
языке Ассемблера, может принять за единицу памяти произвольные
цепочки смежных битов (до 32 бит), размещенных в любой ее
области (с игнорированием байтных границ). Такая единица назы-
вается двоичным полем переменной длины.
Для указания нескольких битов внутри байта, слова, длин-
ного слова или тетраслова определяются номера первого и послед-
него битов, разделенные двоеточием. Например, биты второго
байта длинного слова обозначаются как 15:8. (Такое обозначение
допускается только в пояснениях и документации, а не для
обращения к двоичным полям в командах.)
Виртуальная; память. В мультипрограммной системе, подобной
VAX, в основной памяти одновременно находятся фрагменты
программ и данных нескольких пользователей. Однако программист
может написать программу, размер которой превышает емкость
основной памяти. В VAX существует система виртуальной памяти.
Согласно одному из определений, термин «виртуальный»1 означает
«нечто функциональное или эффективное, но не формальное».
Благодаря организации виртуальной памяти пользователь (и прог-
Буквально — кажущийся,— Примеч. пер.
24
рамма) может полагать, что ЭВМ имеет намного больший объем
основной памяти, чем в действительности. Поскольку в любой момент
времени используется только незначительная часть программы и ее
данных, то именно эту часть и следует хранить в основной памяти,
а все остальное целесообразно разместить на диске. Программы
делятся на сегменты, называемые страницами, по 512 байт каждый.
Когда требуется страница, отсутствующая в основной памяти,
процессор и операционная система фиксируют этот факт и загружают
нужную страницу из устройства внешней памяти. В системе
VAX виртуальная память может содержать до .4,3 млрд. байт.
Ограничение объема виртуальной памяти объясняется тем, что ад-
реса имеют длину 32 бита и максимальное представимое число,
составляет 4294967295. Реальный объём зависит от характеристики
устройств внешней памяти, имеющихся в конкретной установке.
Ячейки памяти, фигурирующие при разработке ассемблерных
программ, оказываются виртуальными ячейками. Процессор и опера-
ционная система преобразуют обращения к виртуальным ячейкам
в обращения к физической памяти. Рассмотренные выше единицы
памяти относятся как к виртуальной, так и к физической памяти.
Представление данных в памяти. Читателю должно быть со-
вершенно ясным, что из-за двоичной структуры памяти все исполь-
зуемые типы данных (целые числа, числа с плавающей точкой,
символьные цепочки и т. д.) и машинные команды должны быть
закодированы с помощью единиц и нулей. Для одних типов данных,
например целых чисел, во многих ЭВМ принято стандартное
кодирование, для других же оно отсутствует. Ниже при рассмот-
рении каждого типа данных будет дано его представление
(или кодирование). Мы также опишем кодирование команд.
В различные моменты времени одна и та же область памяти
может хранить различные типы данных. Каким образом узнать
о том, что конкретная ячейка содержит знаковое число, число с
плавающей точкой, команду и^и что-либо еще? Просто глядя на це-
почку битов, ответить на этот вопрос невозможно; мы (и ЭВМ)
должны знать, какого типа данные находятся в ячейке, прежде
чем осуществить интерпретацию ее содержимого. Обычно тип данных
определяется из того контекста, в котором они используются.
Чтобы подчеркнуть необходимость знания контекста или типа данных
для их правильного дешифрования, покажем шесть различных
интерпретаций одного и того же длинного слова:
Шестнадцатеричное представление длинного слова 63654440
Целое число в формате длинного слова 1667580992
Два целых числа в формате слова 17472 и 25445
Четыре целых числа в формате байта 64, 68, 101 и 99
Команда ADDF2 (R5)[R4],(R3)
Символы ©Dec
Число с плавающей точкой 192.38826
'Приведённые значения не исчерпывают весь список допустимых
интерпретаций длинного слова. Оно может содержать адрес, часть
25
упакованного десятичного числа или числа с плавающей точкой
двойной точности и т. д.
Далее в книге часто приводятся диаграммы или списки содержи-
мого ячеек памяти, иллюстрирующие действия команд. Данные
показываются в тех единицах памяти, размер которых соответ-
ствует рассматриваемой задаче. (Если размер используемых единиц
памяти явно не указан, читатель-должен представить его из показан-
ной информации.) Важно помнить, что машинная память — это
единая длинная последовательность пронумерованных байтов.
Количество данных, необходимых для вычислений или запоминания
результата, зависит от применяемой команды, а не от диаграмм,
демонстрирующих ее действие.
2.2. Центральный процессор
Центральный процессор CPU осуществляет дешифрование и вы-
полнение машинных команд. Дешифрование команд подразумевает
определение требуемой операции и вычисление адресов операндов.
После выборки операндов CPU выполняет операцию с помощью
своих арифметических и логических устройств. Процессор также
следит за работой программы и за ее состоянием.
Стандартная система машинных команд VAX насчитывает более
240 команд, в частности, инициирующих арифметические и логичес-
кие операции над целыми числами и числами с плавающей точкой,
арифметические и логические операции над упакованными десяти-
чными числами (это еще один формат для чисел), операции над
символами и двоичными полями, управление ходом выполнения
программы (например, проверка и условный переход), операции
над специальными структурами данных, называемых стеками и очере-
дями, специальные операции, например редактирование печатаемых
данных, и множество других.
В составе CPU (а не в основной памяти) имеется специаль-
ная запоминающая ячейка, называемая общим регистром'.
Доступ к данным, находящимся в регистрах, и их модификация
осуществляются гораздо быстрее, чем к данным из ячеек
памяти. В систему VAX входит 16 общих регистров, содержащих
по 32 бита, которые нумеруются справа налево от 0 до 31.
Первые 12 регистров RO, R1.... R11 применяются в ассемблер-
ных программах для временного хранения адресов, промежуточных
результатов вычислений и т. д. Оставшиеся четыре регистра
выполняют специальные функции. Это регистры АР (указатель
аргументов), FP (указатель кадра), SP (указатель стека) и PC
(программный счетчик). Регистры АР, FP и SP используются для
связи процедур и подпрограмм, что подробно рассматривается
1 Регистр общего назначения (РОН).—Примеч. пер.
26
в гл. 9. Указатель стека применяется, кроме того, и для других
целей.
Во многих ЭВМ функцией программного счетчика является
указание адреса следующей выполняемой команды. При выборке
команды CPU быстро определяют ее длину и автоматически
корректируют PC, поэтому при завершении текущей команды
программный счетчик уже адресует следующую команду1. Этот
принцип реализуется и в программном счетчике системы VAX,
но из-за гибкого формата команд, затрудняющего определение
длины команды, при ее выполнении обычно приходится корректи-
ровать PC несколько раз. Сначала счетчик адресует начало команды
где находится код операции, затем по очереди указывает на те
ее поля, которые содержат спецификаторы каждого из операндов, и
наконец, адресует начало следующей команды.
В машинных командах обращения к регистрам кодируются чис-
лами от 0 до 15; регистры АР, FP, SP и PC закодированы
от 12 до 15 соответственно.
Обозначение группы битов в регистре совпадает с обозначением
аналогичных групп в единице памяти — указываются номера первого
и последнего битов, разделенные двоеточием.
Хотя общие регистры R0-R11 доступны программисту, работаю-
щему на языке Ассемблера, при выполнении некоторых команд
содержимое этих регистров изменяется. Для нескольких команд
ввода-вывода используется регистр R0, а для ряда других — регист-
ры R0—R5. Поэтому программистам рекомендуется большей частью
применять регистры R6—R11. Регистры R0—R5 могут служить
только для кратковременного хранения данных.
Общий регистр содержит то же число битов, что и длинное
слово. Когда команды, оперирующие байтами или словами,
используют данные из общего регистра, они привлекают его правый
байт или правое слово. Ниже (в табл. 2.1) показано назначе-
ние общих регистров.
В системе VAX имеется множество специализированных регист-
ров, с помощью которых CPU и операционная система выполняют
команды, управляют ресурсами и программами, осуществляют ввод
и вывод данных. Большинство этих регистров недоступно пользо-
вателю; они оказываются настолько «за сценой», что в настоящей
книге не рассматриваются. Однако один из них частично досту-
пен для ассемблерных команд, и программисту, работающему на
языке Ассемблера, имеет смысл с ним познакомиться. Этим
регистром является длинное слово состояния процессора PSL,
которое содержит несколько флагов состояний. Пользователь может
оперировать флагами, находящимися в младшей половине PSL,—
так называемом слове состояния процессора PSW; старшая половина
. 1 Конечно, здесь речь идет о естественном порядке выполнения команд.—Примеч.
пер.
27
PSL зарезервирована для системы. Примером флага первого типа
служит код условия переполнения; он устанавливается, когда
результат арифметической операции слишком велик для ячейки-
получателя, указанной в команде. Программа может проверить код
условия и предпринять соответствующее действие. Пример флага
второго типа — флаг, определяющий уровень привилегий программы.
Операционная система имеет больший уровень привилегий, чем
обычный пользователь и может выполнять команды, недоступные
пользователю, включая и те, которые изменяют уровень привилегий.
Регистр PSL содержит и некоторую критическую информацию.
Ее необходимо сохранять с тем, чтобы при прерывании програм-
мы дать другой программе шанс на использование CPU.
Содержимое PSL автоматически запоминается в памяти, а при возоб-
новлении выполнения программы восстанавливается. Более подроб-
ные сведения о регистре PSW будут приведены при рассмотрении
соответствующих команд.
2.3. Ввод и вывод
Операции ввода и вывода оказываются намного сложнее арифме-
тических и логических операций, выполняемых CPU. Объясняется
это тем, что при вводе-выводе процессору приходится взаимодей-
ствовать с устройствами, внешними по отношению к нему, на-
пример с дисковыми и ленточными накопителями, терминалами и
принтерами. Из-за разнообразия устройств, имеющихся в конкретной
установке, и уникальности правил форматирования и передачи
данных в каждом из них для описания операций ввода-вывода
необходимо определять множество параметров. По ряду причин
пользователь не обладает привилегиями и не несет ответствен-
ности за непосредственное программирование ввода-вывода.
Подавляющая часть работы выполняется системными программами,
к которым он может обращаться. Информация о- физических
характеристиках устройств ввода-вывода, физическом размещении и
форматах файлов находится в системных программах и «невидима»
пользователю. Последний может считать, что данные организованы
таким образом, который наиболее удобен для конкретного приме-
нения, и отвечает лишь за определение этой логической струк-
туры.
Однако дело не только в том, чтобы освободить пользова-
теля от программирования множества деталей ввода-вывода.
Существует еще одна причина, по которой ему не разреша-
ется прямо управлять устройства ми ввода-вывода в мультипрограм-
мной системе: необходимо предотвратить возможнее столкновения
интересов различных пользователей. Представим себе, например,
что две одновременно выполняющиеся программы выводят данные
28
на один и тот же принтер. Данные окажутся перемешанными
и почти бесполезными для каждого из пользователей. Именно
поэтому операциями ввода-вывода, которые запрашивает программа,
управляет операционная система.
В системе VAX предусмотрена утилита RMS, называемая
службой управления записями. Эта утилита обеспечивает для пользо-
вателя организацию файлов и управление операциями ввода-
вывода. Хотя она и облегчает пользователю выполнение опе-
раций ввода-вывода, работа с ней все-таки требует определенных
знаний и навыков. Частично утилита RMS описана в гл. 15,
а в гл. 5 рассмотрены очень удобные в обращении макро-
команды, реализующие простой ввод-вывод.
2.4. Заключение
Итак, ЭВМ имеет три подсистемы: память, центральный процессор
и подсистему ввода-вывода.
Биты в основной памяти многих ЭВМ объединены в группы
по 8 бит, называемые байтами. Для хранения больших объемов
данных разрешается группирование байтов. Команды VAX
могут оперировать словами (2 байта), длинными словами (4 байта),
тетрасловами (8 байт) и октасловами (16 байт). Однако
команды, манипулирующие октасловами, имеются не во всех моделях
VAX. Указанные единицы памяти могут начинаться с любого
байта (за исключением, конечно, ее последних ячеек, где может
не хватить места), но обработка оказывается более эффективной,
если адрес первого байта кратен длине слова. Биты в байте,
слове, длинном слове и тетраслове нумеруются справа налево,
начиная с нуля. Для представления символьных цепочек и упа-
кованных десятичных данных байты можно группировать и иным об-
разом. Допускается группировать до 32 бит безотносительно к гра-
ницам байтов с образованием так* называемого двоичного поля
переменной длины.
Байты памяти нумеруются последовательно от 0 до значения,
на единицу меньшего общего числа байтов в памяти. Назначенное
байту число называется его адресом. При группировании байтов
для создания больших единиц памяти их адресами считаются адре-
са байтов с меньшими номерами.
В VAX применяется система виртуальной памяти. Программист
может считать, что объем памяти намного больше реально имею-
щегося. Часть программы хранится на диске, и по мере необхо-
димости с него считываются блоки, называемые страницами.
Система осуществляет преобразование содержащихся в программе
виртуальных адресов в физические, по которым в основной
памяти фактически хранятся данные и команды.
Поскольку любые данные закодированы с помощью нулей и
29
единиц, для интерпретации этих данных программист (и ЭВМ)
должен знать контекст, в котором они используются.
Центральный процессор дешифрует и выполняет машинные
команды.
Регистр представляет собой специальную запоминающую ячейку
в составе CPU, доступ к которой происходит быстрее, чем к ячейкам
основной памяти. Из 16 общих регистров 12 регистров (R0—R11)
программист, работающий на языке Ассемблера, может применять
для временного хранения адресов и промежуточных результатов
вычислений. Оставшиеся четыре общих регистра (АР, FP, SP и PC)
выполняют специальные функции, причем первые три в основном
предназначены для связывания процедур и подпрограмм, а регистр
PC служит программным счетчиком. Программный счетчик содержит
адрес следующей выполняемой команды или следующего фрагмента
текущей команды. Функции регистров обобщены в табл. 2.1.
Таблица 2.1
Общие регистры
Регистр Назначение
R0-R5 R6—R11 AP(R12) FP(R13) SP(R14) PC(R 15) Общего назначения; модифицируются некоторыми коман- дами Общего назначения Указатель аргументов (для выполнения процедур) Указатель кадра (для выполнения процедур) Указатель стека (для выполнения процедур и других целей) Программный счетчик (явно программистом не исполь- зуется)
Специальный регистр CPU —Длинное слово состояния процессо-
ра PSL — содержит разнообразные флаги и коды условий,
которые обеспечивают информацию о состоянии программы.
Ввод и вывод данных оказываются очень сложными операциями,
так как с их помощью осуществляется взаимодействие между ЭВМ
и «внешним миром». Большинство мелких деталей программирова-
ния ввода-вывода реализует операционная система.
Глава 3
Двоичные
и шестнадцатеричные целые числа
3.1. Двоичная и шестнадцатеричная системы счисления
Все находящиеся в памяти данные закодированы с помощью ну-
лей и единиц, поэтому хранение положительных целых чисел в двоич-
ном представлении вполне естественно. Для анализа и интерпретации
содержащихся в памяти данных и команд возможно оперировать
длинными двоичными цепочками либо преобразовывать двоичные
числа в десятичные, и наоборот. Оба эти приема, однако, сопря-
жены с ошибками и потерей времени. В качестве промежуточ-
ной между двоичной и десятичной системами счисления удобно
воспользоваться шестнадцатеричной системой. Преобразования дво-
ичных и шестнадцатеричных чисел весьма просты, но запись в
шестнадцатеричной системе компактнее, чем в двоичной, — для
представления числа требуется в четыре раза меньше разрядов. Во
многих ситуациях, где обычно применяется десятичная система,
почти столь же легко применима и шестнадцатеричная, поэтому
частых преобразований десятичных и шестнадцатеричных чисел
удается избежать. В настоящем разделе рассматриваются принципы
построения позиционных систем счисления, алгоритмы преобразо-
ваний чисел из одной системы счисления в другую и алгоритмы
шестнадцатеричной арифметики.
Использование в системе счисления позиционного представления
предполагает, что значение цифры зависит от ее позиции в пред-
ставлении числа. Например, цифра 3 в числе 437 обозначает
тридцать, а в числе 3692 — три тысячи. Примером непозиционной
системы служит римская система счисления, в которой, например,
X всегда эквивалентно десяти (или минус десяти). Значение,
представляемое цифрой в позиционной системе счисления, равно
собственно значению цифры, умноженному на степень основания
применяемой системы счисления. Если число записано в виде
dndn_i...d2dtda.
31
где dt — цифры, то для всех I, i = 0...n, di представляет собой зна-
чение цифры dit умноженное на Z-ю степень основания. Следо-
вательно, в десятичной системе правую позицию можно назвать
разрядом единиц (10°), следующую позицию — разрядом десятков
(10’), далее — разрядом сотен (10г) и т. д. В системе с произвольным
основанием г правый разряд есть разряд единиц (г°), следующий —
разряд г1, затем — разряд г2 и т. д. Записанное выше число при
основании г имеет следующее десятичное значение:
(<Xr")+ (<_|Xr',_,)+...+ (d2Xr2)+ (diXr^+rfo. (3.1)
где di — десятичное значение цифры dt при основании г, a I изме-
няется от 0 до п. Позже мы вернемся к этой формуле при
рассмотрении примеров.
Шестнадцатеричная система счисления — это позиционная сис-
тема с основанием 16. Первые разряды справа в шестнадцате-
ричном числе есть разряды 1, 16, 256 и 4096. (Полезно знать
первые степени числа 16.)
Если г — основание используемой системы, потребуются цифры
(т. е. некоторые знаки) для явного представления чисел от 0 до г—1,
так как г можно представить, помещая 1 в разряд со значе-
нием г и 0 в разряд единиц, а большие числа представляются
аналогично. Когда основание не превышает десяти, можно восполь-
зоваться обычными цифрами от 0 до г—1, но в случае
оснований, больших десяти, придется изобретать новые цифры.
Общепринятыми в шестнадцатеричной системе являются цифры
0, 1, 2.... 8, 9, А, В, С, D, Е и F, причем буквы А—F
представляют собой десятичные числа от десяти до пятнадцати.
Преобразования двоичных и шестнадцатеричных чисел. С по-
мощью четырех битов представляются числа от 0 до 15. Соот-
ветствие между двоичными наборами, а также десятичными чис-
лами и шестнадцатеричными представлениями приведено в табл. 3.1.
Каждое четырехбитовое число соответствует одной шестнадцате-
ричной цифре, и наоборот, поэтому шестнадцатеричную запись
можно считать сокращенной записью двоичных чисел. Для преоб-
разования шестнадцатеричного числа в двоичное следует просто
заменить каждую цифру в этом числе ее двоичным эквивалентом.
Например, преобразование числа 2B70F3 осуществляется так:
2 В 7 0^ F 3
0010 1011 0111 ббоо 1111 ООН
Первые два нуля можно опустить, не изменяя значения числа, одна-
ко шестнадцатеричный нуль следует кодировать четырьмя
двоичными нулями. Конечно, старшие нули в кодировании каждой
шестнадцатеричной цифры, за исключением самой левой, необходимо
сохранять.
32
Таблица 3.1
Двоичные и десятичные значения шестнадцатеричных цифр
Двоичные Десятичные Шестнадцатеричные
0000 0 0
0001 1 1
0010 2 2
ООП 3 - 3
0100 4 4
0101 5 5
оно 6 6
0111 7 7
1000 8 8
1001 9 9
1010 10 А
1011 И В
1100 12 С
Ц01 13 D
1110 14 Е
1111 15 F
Для преобразования двоичного числа в шестнадцатеричное
рассмотренная процедура выполняется в обратном порядке — нужно
заменить каждую группу.из 4 бит на шестнадцатеричную цифру с тем
же значением. Так как число битов может оказаться не кратным
4, группирование битов следует начать с правого конца цепочки.
Если левая группа содержит менее 4 бит, необходимо «пристроить»
слева дополнительные нули.
Рассмотрим следующий пример:
1 1010 0100 0111 0101 ООН оою
1 А 4 7 5 3 2
Правильность методов преобразования мы.не доказывали, и вмес-
то формального доказательства (которое предоставляется читателям
с математическими наклонностями) покажем на примере их суть.
Рассмотрим цифру 5 в числе 1А47532. Поскольку цифра 5 находится
в третьем разряде справа, она представляет значение 5Х162.
Соответствующие биты в двоичной цепочке 0101 находятся в разря-
дах от 28 до 2", поэтому вычислим их значение:
(ОХ2")+(1Х2'°)+(ОХ29)+(1Х28) =
= [(0х23) + (1 Х22) + (0х2‘) + (1 X2°)J Х28 = 5Х 162.
Получается то же значение, что и у цифры 5 в числе 1А47532.
Преобразование шестнадцатеричных и двоичных чисел в десятич-
ные. Выражение (3.1) применяется для преобразования недесятич-
ных чисел в десятичные. Пользоваться им очень легко, имея таблицу
степеней основания (или вычисляя их) и производя сложение и
умножение.
Пример. 3.1 Л
Е240716 = (14 X 164) + (2 X 163) + (4 X 162) + (0х 16') + (7Х 16°) =
= 917504 + 8192+1024 + 7 = 926727
2 Зак. 821
33
100111012= (I Х27) + (ОХ 2е) + (0x2®)+(1 Х24) + (1 х23) +
+ (1Х22) + (0х2') + (1х2°)= 128+16+8+4+1 = 157
Возможно исключить операции умножения, построив таблицы
приемлемого размера, которые содержат значения dXp для всех
ненулевых цифр d и каждой степени р. Пример такой таблицы для ос-
нования 16 приведен в приложении В. Ее столбцы соответствуют
степеням числа 16 от 0 до 7, что достаточно для преобразо-
вания в десятичную систему длинного слова. Таблица заполнена
только десятичными числами. Пусть длинное слово содержит
00014A2Ei6 и необходимо найти его десятичный эквивалент.
Нули старших разрядов показывают, что биты никогда не бывают
«пустыми» — они всегда содержат значения: 0 или 1. Преобразо-
вание с помощью таблицы выполняется следующим образом.
Анализируя число справа, отыскиваем в таблице самый правый
столбец 16° и строку для цифры Е — на их пересечении
находится значение 14. Записываем его, а затем переходим в
длинном слове к соседнему разряду слева и пользуемся столбцом
16'; в строке для цифры 2 содержится значение 32. Запишем
и это число. Осуществляем дальнейшие продвижения по длинному
слову и таблице; в строке для цифры А находим 2560. Для
цифр 4 и 1 отыскиваем значения 16384 и 65536 соответственно.
Так как нули в любом разряде имеют значение 0, просмотр
таблицы закончен и теперь нужно сложить найденные значения
(рис. 3.1). В
00014А2Е
00014А£Е
00014А2Е
00014А2Е
00014А2Е
14
4-
32
+
2560
+
16384
+
65536
84526
Преобра-
Рис. 3.1.
зование шестнадца-
терично'го числа в
десятичное. Знак
вставки (А) пока-
зывает обрабатыва-
емую цифру
результате получается число 84526.
На практике не потребуется отыскивать
элементы для всех пяти ненулевых цифр.
После знакомства с шестнадцатеричными
числами первые три разряда преобразовать
очень легко. Цифра Е равна 14, 2 в разряде
16* равно 32, а умножение на десять (А) эле-
ментарно; нужно помнить только, что 162
равно 256. Во многих ситуациях гораздо эф-
фективнее «поработать головой», чем прибегать
к таблицам или пунктуально следовать
алгоритму.
Отметим, что первая строка в таблице со-
держит степени числа 16, которые полезны не
только для преобразования. Если, например,
необходимо определить, сколько чисел можно
представить четырьмя шестнадцатеричными
разрядами (слово в памяти), достаточно отыскать 164, так как любой
из разрядов принимает одно из 16 значений. Этим объясняется тот
факт, что диапазон представимых в слове беззнаковых целых
чисел составляет 0—65535.
Преобразовать двоичные числа в десятичные можно аналогичным
методом (т. е. вновь пользуясь выражением (3.1)), но здесь
не потребуются операции умножения, так как цифрами числа могут
34
быть только 0 и 1. Следовательно, единицы просто показывают,
какие степени числа 2 необходимо суммировать. Таблица преобразо-
вания оказывается гораздо короче, но значительно шире; для
вычисления значений длинного слова будут нужны степени от 2° до
231; они также приведены в приложении В. (Большая часть информа-
ции, содержащаяся в таблицах приложения В, имеется в руководстве
VAX-11 Programming Card.)
Еще один способ преобразования двоичного числа в десятичное
заключается в элементарном переводе его в шестнадцатеричное
число с последующим преобразованием результата в десятичную
систему. При наличии таблицы преобразования этот способ
оказывается более простым, так как приходится суммировать
меньше значений (максимум восемь вместо 32 для длинного
слова). Оба способа иллюстрируются рис. 3.2.
Проблема. Преобразовать двоичное число 110100010000001110
в десятичное
Метод 1. Сложить степени 2, соответствующие единицам в числе
110100010000001110 21 , 2
110100010000001110 22 4
110100010000001110 23 8
110100010000001110 210 1024
110100010000001110 214 16384
110100010000001110 216 65536
110100010000001110 217 131072
Результат « 214030
Метод 2. Преобразовать число в шестнадцатеричное, а затем
воспользоваться таблицей шестнадцатеричного преоб-
разования
Преобразуем число в шестнадцатеричное:
ДЛ Р1Р°- Р100. рооо, J 11 0,
3 4 4 0 Е
Пользуемся таблицей:
3440Е 14
А +
3440Е 1024
3440Е 16384
3440Е 196608
214030 Такой же результат
Рис. 3.2. Преобразование двоичного числа в десятичное
2*
35
Преобразование десятичных чисел в шестнадцатеричные.
Выполняя преобразование десятичных чисел в числа другой системы
счисления, вместо умножения мы выполняем деление. Ниже рассмат-
риваются два способа преобразования десятичных чисел в шестнад-
цатеричные. Первый из них считается универсальным для преобразо-
вания десятичных чисел в числа любой системы, а второй
опирается на таблицу шестнадцатеричного преобразования (но при
наличии соответствующих таблиц он также допускает обобщение).
Первый способ основан на использовании соотношения для вычи-
сления значения (n+ 1)-разрядного числа, представленного в системе
счисления с основанием г. Отметим, что в этом соотношении
значение = (rfnXr") + (dn_4 Хг"-1) + •.• + (52Хг2) + (djXr^+do
все составляющие делятся на основание, за исключенйем do —
младшей цифры шестнадцатеричного числа. Так как do находится
в диапазоне от 0 до г—1, при делении на основание эта
цифра окажется остатком. Следовательно, имея десятичное число,
можно найти младшую цифру его шестнадцатеричного представле-
ния, разделив число на 16 и получив остаток. Все арифметичес-
кие операции выполняются в десятичной системе.
Частное от деления имеет вид:
(rf„Xr"-')+ (d„_,Xrn-2) +...+ (rfjXr'j+d, .
Очевидно, все составляющие этого числа, за исключением di, без
остатка делятся на г, поэтому di будет остатком от деления
нового числа на г. (Если di=0, при делении на г получается
остаток, равный 0.) Новое частное равно
(^Хг"-2) + (<_1Хгп-3) +...+ (й3Хг')+^ •
Процедуру деления на основание и нахождения остатка
можно продолжить до получения всех цифр представления в системе
с основанием г исходного десятичного числа. Процедура закан-
чивается при получении нулевого частного. Рассмотренный способ
преобразования представлен на рис. 3.3.
Результат 2А02
Рис. 3.3. Преобразование десятичного числа 8354 в шестнадца-
теричное
36
Второй способ преобразования десятичных чисел в шестнадцате-
ричные основан на применении таблицы преобразования из прило-
жения В. Прежде всего определим, сколько шестнадцатеричных
цифр будет в результате и какова цифра старшего разряда.
Обозначим преобразуемое десятичное число через k. Найдем в
таблице наибольший элемент, не превышающий k. Столбец,
в котором находится это число, показывает количество разрядов в
шестнадцатеричном представлении k: оно на единицу больше сте-
пени 16, находящейся 'в верхней части столбца. Шестнадцатерич-
ная цифра в найденной строке окажется старшей цифрой
результата. Рекомендуется отметить места для каждого разряда и
выписать цифру старшего разряда. Далее следует вычесть из k най-
денное в таблице число и считать, что теперь k равно полу-
ченной разности. (Арифметические операции выполняются в десятич-
ной системе.) Передвинемся в таблице на один столбец вправо и сно-
ва найдем значение, не превышающее k. Если все значения в данном
столбце слишком велики, то следующая цифра равна 0 и нужно пе-
рейти к другому столбцу. В противном случае цифра содержится в той
строке, где находится наибольшее число, не большее k. Теперь вновь
необходимо вычесть это число из k и обозначить разность через.k.
Процедура поиска цифр продолжается вплоть до заполнения всех
разрядов числа (рис. 3.4).
Проблема: Преобразовать десятичное число 584972 в шестнадцатеричное.
Пусть кз584972.
Максимальный элемент в таблице, не больший к, равен 524288. Он находится
в строке 8 столбца 164, поэтому 16-ричный результат будет иметь 5 цифр
и первая из них равна 8. Выписываем:
8________ Вычитаем: к» 584972
524288
Теперь к® 60884
В столбце 163 находим 57344 — наибольшее число, не превышающее к. Оно на-
ходится в строке Е, поэтому записываем Е как следующую цифру:
8Е Вы читаем: к® _60684
57344
Теперь к ® 3340
В столбце 162 находим 3328 в строке D, поэтому получаем
8ED Вычитаем: ks 3340
~3328_
Теперь ks 12
Все числа в столбце 161 больше 12, поэтому следующая цифра равна нулю
8E_D0_
Наконец, в столбце 16° (или по памяти) находим, что 12 равно С. Следователь-
но, результирующее число
8ED0_C_
Рис. 3.4. Преобразование десятичного числа в шестнадцатеричное
с помощью таблицы преобразования
37
Преобразование десятичных чисел в двоичные. Десятичное число
можно преобразовать, в двоичное с помощью любого из двух
рассмотренных выше способов. Разумеется, при втором способе
придется воспользоваться таблицей степеней 2. Третий же заклю-
чается в том, чтобы сначала преобразовать десятичное число в
шестнадцатеричное, а затем элементарно заменить каждую
цифру четырехбитовым двоичным эквивалентом.
Шестнадцатеричная арифметика. Иногда требуется складывать,
вычитать и даже умножать шестнадцатеричные числа. Конечно,
можно преобразовать числа в десятичные, выполнить над ними ту или
иную операцию, а результат привести к шестнадцатеричной системе.
Однако этот способ довольно громоздок и чреват ошибками.
Оказывается, шестнадцатеричная арифметика не слишком отлича-
ется от десятичной. Правила ее остаются теми же, но нужно помнить,
что необходимость в переносе возникает при превышении суммой
в каком-либо разряде числа 15 (а не 9) и что заем при вычи-
тании соответствует 16, а не 10. При сложении и вычитании
цифр проще всего мысленно превращать «буквенные» цифры
(А—F) в десятичные и производить операции в десятичной сис-
теме счисления. На рис. 3.5 и 3.6 показаны примеры сложения
и вычитания шестнадцатеричных чисел.
При умножении шестнадцатеричных чисел один из сомножителей
часто содержит всего один разряд, равный 1, 4 или 8. Проще
всего производить поразрядное умножение в десятичной системе,
выполняя в уме преобразования шестнадцатеричных и десятичных
чисел. Пример умножения представлен на рис. 3.7.
Выбор шестнадцатеричной системы. В начале этого раздела упо-
миналось, что шестнадцатеричная система удобна как сокращен-
ная форма представления двоичных чисел. Было показано, что
каждая цифра шестнадцатеричного числа точно соответствует
Проблема: Сложить 30Е4 и 29А7 30Е4
Выполняем сложение спреве нелево: 4+7° 11 или В. R 30Е4
В
Е + Аревно 14+10-24. 1
24 ° 16+8, поэтому пишем 8 и делаем перенос: . 30Е4
*
8В
1
1+9° 10 или А. - ж 30Е4
29А7
А8В
1
3+2-5 А 30Е4
5А8В
Рис. 3.5. Сложение шестнадцатеричных чисел
38
Проблема: Вычесть 19В8 из 30Е4
30Е4
” 19В8
4 меньше 8, поэтому необходимо занимать 16 D20
из следующего разряда. В нем остается D, _ 30Е4
а в данном разряде получаем 16+4=20. 19В8
20-8-12 или С, 6
” 19В8_
С
D—В=2
D
30Е4
“ 19В8
2С
2 16
О меньше 9, приходится занимать 16.16-9»7 30Е4
” 19В8_
72С
Наконец, 2—1=1
2
30Е4
” 19В8^ “
172С
Рис. 3.6. Вычитание шестнадцатеричных чисел
Проблема: Умножить 4СА9 на 8
4СА9
х 8
8x9=72, но этот результат необходимо преобра-
зовать в 16-ричный. 72=64+8 и 64=4x16, поэтому
пишем 8 и переносим 4
4
4СА9
х__8_
8
Теперь 8x10+4=84 и 84=5x16+4, поэтому пишем
4 и переносим 5
54
4СА9
х___8_
48
8x12+5=101 и 101=6x16+5, поэтому пишем 5
и переносим 6
654
4СА9
х__ 8_
548
8x4+6=38 или 261в, поэтому окончательно
654
4СА9
х__8_
26548
Рис. 3.7. Умножение шестнадцатеричных чисел
39
группе из .четырех бит, что обеспечивает элементарные преобразова-
ния двоичных и шестнадцатеричных чисел. Но столь же просты
преобразования двоичных и восьмеричных, двоичных и 32-разряд-
ных чисел: если основание есть степень двух, то одна цифра
в системе с таким основанием точно соответствует группе битов.
Поэтому в некоторых ЭВМ вместо шестнадцатеричной системы
используют восьмеричную. Выбор зависит от архитектуры машины.
В VAX имеются 16 регистров, пронумерованных от 0 до 15.
Следовательно, обращение к регистру кодируется в команде че-
тырьмя битами или одной шестнадцатеричной цифрой. Стандартная
система команд VAX насчитывает немногим менее 256 машинных
команд, поэтому большинство кодов операций представляется
восемью битами или двумя шестнадцатеричными цифрами. Посколь-
ку символ кодируется с помощью 8 5ит, символьные коды
содержат по две шестнадцатеричные цифры. В VAX предусмотрены
16 режимов адресации (т. е. способов определения операндов
команды), и каждый из них можно закодировать одной шестнад-
цатеричной цифрой. При известных форматах машинных команд
(о них речь пойдет в гл. 8) анализ шестнадцатеричного
кода команды позволяет быстро представить ее составные части. Так,
если код команды — 5В53А0, то первая (справа налево) пара симво-
лов АО означает код операции (сложения), а каждая их следующих
двух пар содержит спецификатор (specifier) режима адресации и
номер регистра. Здесь оба операнда адресуются в режиме 5
(операнды находятся в регистрах), и выбираются регистры Rl 1 и
R3. .
В машинах, имеющих восемь регистров, 64 или менее команд (или
около 512 команд), использующих 6 бит для кодирования символов,
целесообразно вместо шестнадцатеричной применять восьмеричную
систему, так как кодирование 3 бит одной восьмеричной цифрой
обеспечит более простую интерпретацию кодов. В некоторых из
таких ЭВМ число битов в слове кратно трем.
3.2. Представление
целых чисел
В современных ЭВМ знаковые целые числа сами могут быть
данными, могут быть частью данных другого типа, например
обозначать порядок числа с плавающей точкой, могут входить в
состав команды, определяя местоположение операндов. Очевидным
представлением неотрицательных целых чисел является двоичная
система счисления, которая и применяется в большинстве слу-
чаев.
Ийтересная проблема связана с представлением отрицательных
целых чисел. Ниже рассмотрены три способа решения этой
проблемы, в том числе и с помощью дополнительного кода, использу-
40
емого в VAX и большинстве других ЭВМ. Все. три способа
имеют общую особенность: один бит в числе резервируется
для изображения знака. Принято выделять для знака левый
(старший) бит: в случае положительных (неотрицательных) чисел
этот бит содержит 0, а в случае отрицательных чисел—1.
Выбор того или иного способа представления чисел определяется
тем, как быстро ЭВМ должна выполнять наиболее распространен-
ные операции над данными. Для каждого способа представления
чисел важно решить вопрос об изменении знака числа и о сложении
двух чисел.
Во всех приводимых ниже примерах используются 16 бит, т. е. сло-
во. Максимальное целое число, с которым мы будем иметь дело,
равно 0111111111111111, или 32767. Однако при всех способах пред-
ставления чисел допускается обобщение на любое количество битов
(больше одного).
Прямой код. Простейший способ введения отрицательных целых
чисел — представить в двоичной системе абсолютное значение числа,
а левый бит зарезервировать для знака. Такой способ называ-
ется представлением в прямом коде (sign-magnitude — буквально
«знак и модуль»). В нем знак и величина (абсолютное значе-
ние) числа представляются раздельно.
Пример 3.2. Представление в прямом коде чисел 92 и —92:
92 0000000001011100
—92 1000000001011100
Изменить знак числа весьма просто — достаточно инвертировать
(или дополнить) знаковый бит. Сложение реализуется обычным
образом, но в двоичной системе счисления. Если оба числа иртеют
один и тот же знак, следует сложить их абсолютные значения
и - присвоить результату общий знак. Если же знаки чисел
различны, необходимо определить большее значение и вычесть из не-
го меньшее, а результату присвоить знак операнда с большим
значением. Особых трудностей здесь нет, но приходится выпол-
нять проверку, принимать решения и осуществлять различные дей-
ствия. Выбор прямого кода оказывается неудовлетворительным,
если ЭВМ должна выполнять миллион и более операций
сложения в секунду.
Обратный код. Следующий способ представления чисел — пред-
ставление в обратном коде (one’s complement — буквально «до-
полнение до единицы»). Сначала абсолютное значение отрицатель-
ного числа необходимо записать в двоичной форме, а затем
инвертировать все биты. Отметим, что при этом знаковый бит
оказывается в состоянии 1, т. е. в том, в каком он и должен
находиться. Далее в примерах по применению прямого и допол-
нительного кодов приводятся шестнадцатеричные эквиваленты дво-
ичных цепочек. Те операции, которые нам иногда приходится
выполнять над двоичными целыми числами (например, интерпрета-
ция и сложение), можно осуществить довольно просто, пользуясь
41
сокращенной записью в шестнадцатеричной форме. Отметим, что
инвертирование бита эквивалентно вычитанию его из единицы, поэ-
тому инвертирование битов, заданных в шестнадцатеричном
представлении, реализуется посредством вычитания шестнадцате-
- ричных цифр из F (или 15ю).
Пример 3.3. Представление в обратном коде чисел 92 и —92:
92 0000000001011100 005С
—92 1111111110100011 FFA3
Для изменения знака положительного или отрицательного целого
. числа следует инвертировать все биты.
Сложение можно выполнять без учета знаков операндов. Операн-
ды складываются как беззнаковые двоичные числа, т. е. знаковые
биты участвуют в операции наравне с остальными. При переносе
единицы из левого бита последняя прибавляется в правый (млад-
ший) бит.
Пример 3.4. Сложение чисел 45 и —92 в обратном коде:
45 0000000000101101 002D
+
(—92). 1111111110100011 FFA3
(—47) 1111111111010000
(перенос 0)
Пример 3.5. Сложение чисел
1637 .0000011001100101
(—101) 1111111110011010
0000010111111111
+ 1
1536 0000011000000000
FFD0
1637 и —101 в обратном коде:
0665
FF9A
10FF
+ 1
0600
Читателю предлагается самостоятельно проверить правильность
сложения в обратном коде отрицательных чисел. Кроме того, подго-
товленный читатель может попытаться доказать справедливость
приведенного выше правила в общем виде.
Сложение в обратном коде ЭВМ выполняет относительно просто
и быстро, так как здесь не требуется принятия решений, а этапы
операции одни и те же (бит переноса всегда прибавляется в млад-
ший бит), но иногда переносится нуль, как в примере 3.4. К сожале-
нию, суммирование бита переноса может вызвать почти такие же
действия, как и сложение операндов. В примере 3.5 прибавление
бита переноса вызвало изменение 10 бит результата. Кроме потерь
на учет переноса, обратный код имеет еще один недостаток, что
почти исключило его применение в ЭВМ. Число 0 имеет вид
0000000000000000. Что произойдет при изменении знака этого числа?
Инвертируем все биты и получаем 1111111111111111. Таким обра-
зом, представление числа —0 отличается от представления числа +0,
хотя алгебраически —0=+0. Два различных представления нуля
затрудняют проверку результатов арифметических операций.
42
Предположим, что требуется сложить два числа и проверить ре-
зультат на нуль, например:
(—8) 1111111111110111 FFF7
+ + +
8 0000000000001000 0008
6 1111111111111111 FFFF
Результат = —0 и не =0.
В некоторых ЭВМ обратный код все же применяется, но при этом
приходится постоянно контролировать результаты арифметических
операций, преобразуя число —0, если оно возникает, в число +0.
Однако в большинстве ЭВМ эта проблема (двух представлений
нуля) решается с помощью дополнительного кода.
Дополнительный код. Для получения дополнительного кода
(two’s complement — буквально «дополнение до двух») отрицатель-
ного целого числа следует сначала записать его абсолютное зна-
чение в двоичной форме, а затем инвертировать все биты и приба-
вить к результату единицу.
Пример 3.6. Представление в дополнительном коде числа —92:
92 000000Q001011100 005С
Инвертировать биты 1111111110100011 FFA3
Прибавить 1 4-14-1
—92 1111111110100100 FFA4
Для изменения знака отрицательного целого числа приведенную
выше процедуру нужно выполнить в обратном порядке. Хотя это и
не очевидно, результат получается правильный. Другими словами,
для нахождения абсолютного значения отрицательного целого чис-
ла, представленного в дополнительном коде, необходимо инверти-
ровать все биты и к результату прибавить единицу.
Пример 3.7. Изменение знака числа —92:
—92 1111111110100100 FFA4
Инвертировать биты 0000000001011011 005В
Прибавить 1 4-14-1
0000000001011100 005С
Таким образом, изменение знака целого числа (положительного
или отрицательного) осуществляется довольно просто и не зави-
сит от знака исходного числа. Но всегда ли этот способ дает пра-
вильный результат? Формальное доказательство справедливости
сформулированного положения приводится в конце настоящего
раздела.
Как же складывать числа в дополнительном коде? Для этого
достаточно просто сложить две битовые цепочки как беззнаковые
целые двоичные числа и проигнорировать перенос из старшего
бита, если он возникает.
43
Пример 3.8. Сложение чисел 92 и —45 в дополнительном коде:
92 0000000001011100 005С
4- 4~ +
(-45) _ 1111111111010011 FFD3
10000000000101111 1002F
Удалить перенос 0000000000101111 002F
Результат равен 47
Пример 3.9. Сложение чисел —102 и —58 в дополнительном коде:
— 102 1111111110011010 FF9A
4-
(-58) 1111111111000110 FFC6
— 160 11111111101100000 1FF60
Удалить перенос 1111111101100000 FF60
Для проверки результата (а он получился отрицательным)
находим его абсолютное значение, инвертируя биты и прибавляя
единицу:
. Результат 1111111101100000 FF60
Инвертировать 0000000010011111 009F
Прибавить 1 4“ । + 1
Результат 0000000010100000 00Л0
27 + 28= 160 10X16=160
Таким образом, изменять знак числа и складывать числа в допол-
нительном коде не сложнее, чем в обратном коде. А как здесь реша-
ется проблема двух представлений нуля? Начнем с 0 и найдем зна-
чение —0 в дополнительном коде:
о оооооооооооооооо
Инвертировать 1111111111111111
Прибавить 1 +1
10000000000000000
Удалить перенос 0000000000000000
В результате мы получаем —0= 4-0, как и следовало ожидать.
В системе VAX дополнительный код применяется для целочис-
ленных данных, которые могут храниться в байтах, словах, длинных
словах или тетрасловах, а также в младших байте и слове общего
регистра или в полном регистре. В гл. 8 показано, что часто требуется
включать целые числа (положительные и отрицательные) в команды,
чтобы указать местоположение операнда. Для этих целей также
применяется дополнительный код.
При рассмотрении операции сложения предполагалось, что ре-
зультат может быть размещен в слове. Но это предположение оправ-
дывается не всегда. Например, при сложении чисел 29676 и 3204,
каждое из которых меньше максимального числа 32767, получается
слишком большое число 32880. Что произойдет после их сложения
в дополнительном коде?
44
29676 0111001111101100 73ЁС
4- +
3204 0000110010000100 ОС 84
32880 1000000001110000 8070
Так как левый бит суммы равен 1, результат будет ошибочно
интерпретирован как отрицательное целое число. Когда результат
арифметической операции не помещается в назначенном ему месте,
команды целочисленной арифметики устанавливают признак пере-
полнения в регистре PSW. Программист должен проверить этот
признак, .если обрабатываемые данные могут вызвать переполне-
ние. Для приведенного выше примера проблема переполнения не
возникает, если воспользоваться длинными словами; в них знако-
вым является бит 31, а не 15.
Таблица 3.2
Представления целых чисел
Двоичный набор Прямой код Обратный код Дополнительный код*
000 0 0 0
001 1 1 1
010 2 2 2
он 3 3 3
100 —0 —3 —4
101 — 1 —2 -3
по —2 — 1 —2
111 —3 —0 — 1
1 В дополнительном коде можно представить «лишнее» отрицательное число. Это справедливо вне зависимости от количества используемых битов.
В табл. 3.2 показана интерпретация всех трехбитовых наборов в
рассмотренных выше кодах; знаковым здесь является бит 2. Отме-
тим, что представления —0 и +0 различны в прямом и обратном
кодах. В дополнительном коде с естественным представлением 0
имеется «лишний» набор. Как видно, дополнительный код позволяет
представить число —4, которое не представимо в прямом и обратном
кодах.
Обоснование правил дополнительного кода. Чтобы доказать,
что инвертирование битов и прибавление единицы всегда изменяют
знак целого числа в дополнительном коде и что правило сложения
чисел в дополнительном коде справедливо, обратимся к другой ин-
терпретации отрицательных чисел в дополнительном коде. Пусть
s — количество битов в представлении чисел (во всех приведенных
в настоящем разделе примерах $ = 16). Обозначим через п отрица-
тельное целое число, а через |п| — его абсолютное значение. Тогда
дополнительный код числа п представляет собой двоичный код
числа 2s—|п|. Этот вывод справедлив, так как инвертирование битов
45
|п| эквивалентно вычитанию |п| из s единиц, которые представляют
число 2s—1. Следовательно, при инвертировании битов
|n| =2S—1—|п|.
Прибавим единицу для получения дополнительного кода:
2s—1—|n| +1 =2S—|и|.
Посмотрим, что получится,, если начать с дополнительного кода
отрицательного числа и выполнить действия в обратном порядке.
Вновь предположим, что число равно п. Начнем с числа 2s—|л|:
п в дополнительном коде 2s—|н|
инвертирование битов (2s—1) — (2s—|n|) = — 1 + |n|
прибавление 1 —l+|n| + l = |n|
-Результат равен —п, т. е. |п|.
Если $ кратно четырем, что справедливо для стандартных единиц
памяти, можно записать шестнадцатеричное представление отрица-
тельного числа п в дополнительном коде как 16s/4—|л|. Аналогичные
вышеприведенным выкладки позволяют сделать вывод о том, что
операции изменения знака и сложения чисел в дополнительном
коде, но записанных в шестнадцатеричной системе, выполнены
правильно.
Пользуясь записью представления отрицательного числа п в
дополнительном коде как 2s—|п|, нетрудно доказать, что сложение
двоичных цепочек и игнорирование переноса дает правильную сум-
му двух чисел (если результат помещается в s битах). Доказатель-
ство этого положения вынесено в упражнения.
3.3. Заключение
Для сокращенной записи двоичных цепочек применяется пози-
ционная шестнадцатеричная система счисления с основанием 16.
Преобразование двоичных и шестнадцатеричных чисел осуществ-
ляется весьма просто: любой четырехбитовый набор соответствует
одной шестнадцатеричной цифре, и наоборот. Существует несколько
способов преобразования двоичных (и шестнадцатеричных) и де-
сятичных чисел; некоторые из них реализуются с помощью таблиц,
приведенных в приложении В.
Шестнадцатеричная арифметика аналогична десятичной, но сле-
дует тщательно следить за переносами и заемами.
Для системы VAX удобно шестнадцатеричное представление
чисел, так как поля машинных команд занимают 4, 8, 16 или 32 бита
и их можно закодировать раздельно с помощью одной, двух, четырех
или восьми шестнадцатеричных цифр соответственно. Кроме того,
символы кодируются восемью битами или двумя шестнадцатерич-
ными цифрами.
В ЭВМ применяются несколько способов представления знако-
вых целых чисел — в прямом, обратном и дополнительном кодах.
Каждый из них предполагает представление положительных чисел
46
в двоичной системе, а левый бит резервируется для знака (0 обозна-
чает «плюс», а 1 —«минус»).
В системе VAX используется дополнительный код. Для обра-
зования дополнительного кода отрицательного числа следует запи-
сать его абсолютное значение в двоичной форме, инвертировать
все биты и прибавить единицу. Можно также записать абсолютное
значение числа в шестнадцатеричной системе, вычесть каждую циф-
ру из F, а затем прибавить единицу.
Достоинства дополнительного кода — однозначное представле-
ние нуля и простота выполнения операций сложения и вычитания
независимо от знаков операндов. Два знаковых числа, представлен-
ные в дополнительных кодах, суммируются так, как будто они явля-
ются беззнаковыми двоичными числами. Перенос из старшего бита
игнорируется. Арифметические операции удобнее производить в
шестнадцатеричной системе, а не манипулировать длинными дво-
ичными цепочками.
Дополнительный код отрицательного целого числа п можно
рассматривать как двоичный эквивалент числа 2s—|п| или как
шестнадцатеричный эквивалент числа 16s/4—|п|, где s — количество
битов в числе.
В том случае, когда результат арифметической операции не по-
мещается в отведенном для него месте, в PSW устанавливается
бит признака переполнения.
3.4. Упражнения
1. Преобразуйте следующие шестнадцатеричные числа в двоичные: а) 2047,
б) D104F, в) FAD, г)ЗА01.
2. Преобразуйте следующие двоичные числа в шестнадцатеричные:
а) 110100011011111001011, б) 111000110001, в) 100000000, г) 11111100000.
3. Найдите десятичные эквиваленты шестнадцатеричных чисел: а) 86Е,
б) 209АВ5, в) F0, г) 1000.
4. Преобразуйте следующие десятичные числа в шестнадцатеричные: а) 80, .
б) 125, в) 3075 (двумя способами), г) 11625042.
5. Найдите десятичные эквиваленты двоичных чисел: а) 110011110,
б) 1111111011. (Попробуйте найти более быстрый способ, чем рассмотренные в
разд. 3.1 способы.)
в. Найдите двоичные эквиваленты десятичных чисел: а) 147, б) 515.
7. Сложите шестнадцатеричные числа: a) 804E + BFD7, б) IFF4-7305.
8. Выполните вычитание шестнадцатеричных чисел: а) 7Е043—48А7, б)
4028—1259.
9. Выполните умножение шестнадцатеричных чисел: а) 20С4Х8, б) 64x4.
10. Докажите, что при замене каждой шестнадцатеричной цифры ее четырех-
битовым двоичным эквивалентом получающееся двоичное число равно исходному
шестнадцатеричному.
11. Найдите шестнадцатибитовые представления числа —156 в прямом, обрат-
ном и дополнительном кодах. Приведите шестнадцатеричное представление
дополнительного кода.
12. Какое десятичное число имеет представление FF79 в дополнительном коде?
Какой тип представляет эго число — байт, слово, длинное слово или тетраслово?
47
13. Найдите десятичные эквиваленты следующих целых чисел, представлен-
ных в дополнительном коде: a) FFFF, б) FE00, в) Е021, г) FFFF2A72.
14. Представьте следующие десятичные числа в дополнительном коде, поль-
зуясь для каждого из них наименьшей единицей памяти (байт, слово или длинное
слово). Результаты покажите в шестнадцатеричной системе: а) —542, б) —25,
б) 256, г) —40800.
15. Какое целое число можно представить в дополнительном коде, пользуясь
восемью битами, но его же с измененным знаком представить нельзя?
. 16. Допустим, задан дополнительный код. Какие наибольшее и наименьшее
числа можно представить в байте? в слове? в длинном слове?
17. Докажите, что сложение дополнительных кодов как беззнаковых двоичных
чисел с игнорированием переноса из старшего разряда всегда дает дополнительный
код суммы (если переполнение исключено).
18. Представьте числа. 37412 и —33316 в дополнительном коде (пользуясь
шестнадцатеричной системой)* и сложите их.
19. Разработайте для ЭВМ эффективный способ вычитания чисел в дополни-
тельном коде независимо от знаков операндов.
20. Сформулируйте правило (или правила) обнаружения переполнения при
сложении чисел в дополнительном коде.
Г л а в a 4
Элементы языка Ассемблера
Большинство исходных ассемблерных операторов содержит сле-
дующие поля (или некоторые из них): поле метки, поле оператора,
поле операнда и поле комментария. В настоящей главе рассматри-
ваются компоненты полей, поясняются наиболее часто используемые
директивы ассемблера и иллюстрируется организация законченной
программы. Поскольку читатель стремится получить достаточно
информации, чтобы как можно скорее начать писать простые за-
конченные программы, все эти вопросы изложены здесь не в полном
объеме. Более подробные сведения приводятся в последующих
главах и в руководствах фирмы DEC. Отдельные машинные команды
даются в примерах без формальных пояснений, так как их общие
функции почти очевидны, а более детально они описываются ниже.
Следует подчеркнуть, что без явных указаний программиста
ассемблер интерпретирует все входящие в команды числа как деся-
тичные.
4.1. Имена и метки
Символические имена обозначают разнообразные объекты.
С их помощью именуются переменные, массивы и другие области
данных, а также отмечаются команды. Имена общих регистров
RO,..., R11, АР, FP, SP, PC и мнемоники команд являются фиксиро-
ванными. Те же имена, которые вводятся для отметки команд и
данных, а также те, которые задает программист, называются
именами, определяемыми пользователем.
Имя может содержать до 31 символа — буквы, цифры и знак
подчеркивания (__), но первым его символом не должна быть цифра.
(Знаки доллара и точки допускаются, однако их следует избегать,
так как они используются в именах, определяемых системой.)
Чтобы контролировать число байтов, требуемых для трансляции
программной секции, в ассемблерах предусматривается счетчик
. 49
ячеек (адресов). В начале ассемблирования каждой программой
секции ассемблер сбрасывает значение счетчика на нуль, а затем
увеличивает его на число байтов, необходимых для каждой трансли-
руемой машинной команды и каждой резервируемой области данных.
Таким образом, значение счетчика всегда представляет собой
адрес следующего доступного байта (относительно начала програм-
мной секции).
Определяемые пользователем имена наиболее часто задаются
(им присваивается значение) путем указания в поле метки коман-
ды. При этом имя должно заканчиваться двоеточием. Значением,
присваиваемым имени, является значение счетчика ячеек в текущий
момент времени. Следовательно, имя относится к ячейке, содержа-
щей команду или данные. Ассемблер запоминает имя и его значение
в таблице имен; когда имя фигурирует в других командах, ассемб-
лер может просмотреть таблицу, найти значение имени и правильно
закодировать адрес при трансляции таких команд. Важно отчетли-
во представлять себе, что для ассемблера значением имени перемен-
ной служит адрес ячейки этой переменной, а не ее содержимое,
т. е. то, что мы обычно считаем значением переменной. Поясним
изложенное следующим примером.
Предположим, что в программе предусмотрены операторы, ко-
торые заставляют ассемблер инициализировать длинные слова в
памяти на указанные значения:
ALPHAi
BETAi
ВАННА1
.LONG
.LONG
.LONG
1186
-28
35
Как показано ниже, при ассемблировании первой директивы
.LONG значение счетчика ячеек было равно 108 (ячейки всегда
даются в шестнадцатеричной системе). Поскольку в длинном слове
наименьший номер имеет правый байт, адреса и метки размеща-
ются справа. Напомним, что длинное слово состоит из четырех
байт, поэтому показанный для каждого длинного слова адрес боль-
ше предыдущего на четыре. Данные в памяти представлены в
шестнадцатеричной системе:
Содержимое памяти Адрес ячейки
000004А2 00000108
FFFFFFE4 ОООООЮС
00000023 00000110
Имя
ALPHA
ВЕТА
GAMMA
Значения имен ALPHA, ВЕТА и GAMMA составляют соответ-
ственно 108, ЮС и ПО, что можно записать в виде ALPHA=108,
ВЕТА = ЮС и GAMMA=110. Неправильно говорить, что ALPHA =
= 4А2|6 и ALPHA = 1186ю, как это принято в языках высокого уров-
ня. Общепринято обозначать содержимое ячейки памяти или ре-
50
гистра, указывая имя (или адрес) в круглых скобках, например
(ALPHA) означает «содержимое ALPHA». В приведенном примере
(ALPHA) = 000004A2i6, (ВЕТА) = FFFFFFE4I6 и (GAMMA) =
= 0000002316- Отметим, что при выполнении программы (ALPHA)
может часто изменяться, но ALPHA не меняется никогда (если,
конечно, не изменяется сама программа).
Предположим теперь, что в программу включена команда, с
помощью которой длинное слово из ячейки, определяемой первым
операндом, передается в ячейку, определяемую вторым операндом:
MOVL ALPHA+4, GAMMA
Что пересылается в длинное слово GAMMA? Конечно, не
000004А6, что было бы указано как (ALPHA)+4. Выражение
ALPHA+4= 10С16 определяет адрес второго длинного слова и по
команде в GAMMA пересылается значение FFFFFFE4. (По сущест-
ву, ALPHA+4 = ВЕТА.)
Рассмотренный пример не только подчеркивает тот факт, что
значением имени является адрес, но и показывает, что имя допуска-
ется объединять с другими элементами и получать выражение,
определяющее местоположение операнда. Правила образования
выражений рассматриваются ниже, пока же следует запомнить, что
имя представляет собой адрес, а не содержимое адреса, и считать
любое приемлемое простое выражение допустимым.
Определяемые пользователем имена можно задавать явно, при-
равнивая их требуемому значению. Это осуществляет оператор
прямого присваивания, имеющий следующий формат:
имя = выражение
Любые появляющиеся в выражении имена должны быть опреде-
лены в программе заранее. Операторы прямого присваивания ис-
пользуются для разных целей, например именования констант для
ассемблера, чтобы было проще разбираться в программе и модифи-
цировать ее. (Напомним, что имена во время выполнейия програм-
мы не существуют; находящиеся в ней константы должны быть раз-
мещены в памяти или в регистре.) Задаваемое оператором прямого
присваивания имя допускается переопределять (т. е. назначать
позже в программе другое значение) любое число раз. Применение
операторов прямого присваивания иллюстрируется далее приме-
рами.
4.2. Операторы
Поле оператора специфицирует операцию или действие, реали-
зуемое ассемблерным оператором. Различают три класса операторов:
машинные команды, ассемблерные директивы и макрокоманды.
Машинные команды транслируются ассемблером в машинный код
51
и во время выполнения инициируют арифметические операции,
пересылки данных и переходы. Ассемблерные директивы представ’
ляют собой инструкции ассемблеру о выполнении разнообразных
служебных задач, резервировании памяти и прочих управляющих
функциях. Макрокоманда — это введенная программистом (или
содержащаяся в системной библиотеке) псевдокоманда, обозначаю-
щая некоторую последовательность машинных команд, ассемблер-
ных директив и (или) других макрокоманд. В соответствии с тер-
минологией фирмы DEC операнды ассемблерных директив и макро-
команд мы будем называть аргументами.
Для выполнения машинной команды ЭВМ требуется некоторая
информация: инициируемая операция; число, местоположение и
типы операндов; тип и местоположение результата. Часть этой ин-
формации содержится в имени команды (неявно), а часть — в спе-
цификаторах операндов. В именах почти всех машинных команд
первые три-четыре буквы указывают выполняемую операцию, а
следующая буква (иногда следующие две буквы) во многих коман-
дах определяет тип(ы) операндов и результата (обычно операнды
и результат имеют один и тот же тип). Типы данных обозначаются
таким образом:
Буква Тип данных
В Байт
W Слово
L Длинное слово
Q Тетраслово
Р Упакованное десятичное число
F Плавающая точка (одинарная точность)
D Плавающая точка (двойная точность)
G Плавающая точка (тип G)
Н Плавающая точка (тип Н)
С Символ
V Двоичное поле переменной длины
Число операндов в одних командах задается неявно, а в дру-
гих — цифрой в конце имени команды. Итак, по имени команды до-
вольно легко понять, какие действия она вызывает. Приведем
несколько примеров:
MULW3 — умножить целочисленные слова, 3 операнда;
CVTLF — преобразовать длинное слово в число с плавающей
точкой;
SUBB2 — вычесть целочисленные байты, 2 операнда (первый
операнд вычитается из второго);
CMPF — сравнить операнды с плавающей точкой;
CLRL -г— очистить длинное слово (т. е. сбросить его содер-
жимое на нуль);
INCW — выполнить инкремент целочисленного слова на 1;
MOVC3 — переслать символы, 3 операнда;
BEQL — выполнить переход, если операнды равны (исполь-
зуется после сравнения).
52
Имена ассемблерных директив должны начинаться с точки
(чтобы было проще отличать их от машинных команд). Имя дирек-
тивы обычно представляет собой полное или сокращенное слово,
определяющее ее функцию. Наиболее часто используемые директи-
вы рассмотрены в разд. 4.4.
Макрокоманды могут быть весьма сложными; с некоторыми из
них мы познакомим.читателя в гл. 5, но подробное описание макро-
команд дается в гл. 11.
4.3. Адресация операндов
Система VAX имеет необычайно широкий спектр режимов адре-
сации, т. е. способов определения местоположения операндов. За
некоторыми исключениями любой режим адресации допускается
использовать в любой машинной команде. Это отличительная черта
ЭВМ семейства VAX. Во многих других ЭВМ каждая команда тре-
бует специфического формата операндов.
В командах, содержащих более одного.операнда, их назначение
указывается порядком записи. Например, в команде вычитания
первый операнд вычитается из второго. Обычно операндом-полу-
чателем — местом, куда направляется результат операции — явля-
ется последний операнд. Почти все режимы адресации мо^кно при-
менять Для определения и местонахождения обрабатываемых дан-
ных, и местоположения результата, но, конечно, существуют и огра-
ничения; например, режим адресации, определяющий константу,
нельзя указать для результата операции.
Операнд может находиться в общем регистре,, в памяти или в
самой команде. Ниже обсуждается несколько простых режимов
адресации применительно ко всем трем случаям. Для спецификации
операндов в памяти предусмотрены дополнительные, более сложные
режимы адресации. Некоторые из них мы рассмотрим здесь, а
остальные — в последующих разделах. '
Регистровый режим: Rn. Регистровый режим предназначен для
указания местоположения операнда в общем регистре. Специфи-
катором операнда служит имя регистра, например RO,..., R11, АР,
FP или SP.
Если задан тип данных длиной менее 32 бит, в частности В и W, ис-
пользуется байт или слово в правой части регистра. Его левая часть
игнорируется и не изменяется командой, оперирующей байтом или
словом. Если же типом данных является тетраслово или число с пла-
вающей точкой двойной точности (64 бита), операнд находится в
двух общих регистрах Rn и Rn+ 1. (Для типов данных длиной более
64 бит требуется соответствующее число регистров, начиная с Rn.)
Общими регистрами целесообразно пользоваться для хранения
промежуточных результатов и наиболее часто требующихся данных,
так как доступ к регистрам осуществляется быстрее, чем к памяти.
53
Пример 4.1. Регистровый режим. Предположим, что содержимое регистра R6
составляет 00280Е45, а регистра R9—FFFFF3A6. После выполнения команды пере-
сылки или копирования
MOVL R6, R9
в обоих регистрах будет содержаться 00280Е45. Команда
MOVW R6, R9
изменит содержимое регистра R9 на FFFF0E45.
Относительный режим: адрес. Относительный режим можно
применять для адресации операндов в памяти. Спецификатором
адреса служит выражение (обычно просто имя), значение которого
представляет собой адрес используемых данных или ячейки, в ко-
торой запоминается результат операции. Название этого режима
мы объясним позже при рассмотрении кодирования машинного
языка.
Пример 4.2. Относительный режим. По команде
MOVC3#32, TITLE, LINE-}-12 ;Переслать заголовок
цепочка символов пересылается из области TITLE в область LINE+12. Второй
и третий операнды заданы в относительном режиме. Первый операнд является
литералом.
Литеральный и непосредственный режимы: 41= число или # вы-
ражение. В литеральном и непосредственном режимах в команде
фигурируют фактические данные, участвующие в операции, а
определяется обычно их местоположение. Операндами здесь могут
быть константы — целые числа или числа с плавающей точкой.
Константу допускается определять числом или выражением (как
правило, просто именем). В дальнейшем мы рассмотрим ограниче-
ния, которым должны удовлетворять выражения. Константе пред-
шествует символ обозначающий непосредственный операнд,
а не его адрес.
В языке Ассемблера спецификаторы операндов литерального и
непосредственного режимов выглядят одинаково, но в машинном
коде они различаются. (Ассемблер выбирает тот или иной специфи-
катор в зависимости от числа битов, необходимого для размещения
константы. Длина литерала составляет б бит, а длина непосредствен-
ного операнда может быть больше. Машинный код подробно описы-
вается в гл. 8.)
Пример 4.3. Литеральный режим. По команде
MOVW #25, R11 ;ЗагрузитЬ максимальный размер в R11
(десятичная) целая константа загружается в правое слово регистра R11:
R11
До выполнения EFFE0972
После выполнения EFFE0019
Это же действие можно выполнить по-другому:
MAX.SIZE • 25
MOVW #MAX_BIZE,R11 । Загрузить максимальный размер в R11
54
Применение имени МАХ_SIZE делает программу понятнее, а нри изменении
максимального размера достаточно модифицировать только оператор прямого
присваивания, и программисту не нужно просматривать всю программу, отыскивая
число 25.
Начинающие программисты часто забывают указывать знак #
для литерального или непосредственного операнда. Так, если запи-
сать команду в виде
MOVW 25.R11 Загрузить максимальный размер в R11
ассемблер закодирует 25 как адрес, и процессор попытается ско-
пировать в Rl 1 слово памяти с адресом 25. Наиболее вероятно, что
при этом во время выполнения команды возникнет ошибка, назы-
ваемая нарушением доступа, так как ячейка 25 находится вне обла-
сти памяти, доступной программе. Ошибка возникает и в том слу-
чае, если без знака # указать и имя МАХ______SIZE, так как оно не
является меткой; МАХ____SIZE равно константе 25.
Иногда удобно определять литерал как символ. Значение лите-
рала представляется кодом ASCII символа. Формат таких литера-
лов имеет вид #Л А /символ/. Например, по команде
MOVB#A А|*|, LINE
код ASCII (Американский стандартный код для обмена информа-
цией) звездочки будет помещен в байт, отмеченный LINE.
Режим перехода: назначение. Назначение перехода можно оп-
ределить выражением; обычно это имя, используемое в качестве
метки команды. Режим перехода задает то место в программе, куда
передается управление, если удовлетворяется некоторое условие.
Если задан безусловный переход или условный переход с удовлетво-
ряющимся условием, адрес назначения загружается в программный
счетчик PC и следующей выполняется команда по этому адресу.
Диапазон переходов относительно текущей команды ограничен; он
рассматривается в главе, посвященной машинным кодам команд
переходов.
В языке Ассемблера режим перехода аналогичен относитель-
ному режиму, но в машинном языке они кодируются по-разному.
Пример 4.4. Режим перехода. Команда безусловного перехода
BRB NEXT
вызывает переход к команде с меткой NEXT.
Следующие три режима адресации определяют операнды в памя-
ти. Читатели, которые стремятся как можно скорее научиться пи-
сать короткие программы, могут опустить этот материал и вернуться
к нему после изучения гл. 5.
Обозначение Rn, используемое при описании в режимах адреса-
ции, относится к любому из регис+ров R0, .... Rl 1 или АР, FP и SP.
Регистровый косвенный режим: (Rn). В этом режиме общий ре-
гистр содержит не сам операнд, а его адрес. Напомним, что адрес
55
или имя регистра, заключенные в круглые скобки, обозначают со-
держимое адреса или регистра. Следовательно, в регистровом кос-
венном режиме местоположение операнда определяется как (Rn),
т. е. содержимым Rn.
Пример 4.5. Регистровый косвенный режим. По команде
ADDW2 R5, (R8) ;Прибавить стоимость пищи к бюджету
правое слово R5 прибавляется к слову, адрес которого находится в регистре R8.
Предположим, что до выполнения приведенной выше команды
данные имеют следующий вид:
Содержимое памяти Адрес ячейки
R5 F543 00000840
EFFE002E FFFF 00000842
R8 FFB9 00000844
00000846 001С 00000846
. 074D ч 00000848
12С0 0000084А
Команда изменит только байты 846 и 847. Так как 002Е + 001С =
= 004А, в результате мы получим
Содержимое памяти Адрес ячейки
004А
00000846
Отметим, что левая половина регистра R5 игнорируется, но,
поскольку длина адреса всегда составляет 32 бита, используется все
содержимое регистра R8. Память представлена здесь в виде списка
cjIob, так как рассматриваемая команда воздействует на слова. В об-
щем память разбивается на единицы, размеры которых соответст-
вуют указанным в конкретном примере. Однако рисунки не должны
вносить путаницы. Для предыдущего примера память можно пред-
ставить как список длинных слов (до выполнения команды):
Содержимое памяти Адрес ячейки
FFFFF543 00000840
001CFFB9 00000844
12C0074D 00000848
Выполнение команды дает тот же результат:
004AFFB9 00000844
Автоинкрементный режим: (Rn) + . Это один из нескольких весь-
ма удобных для обработки данных в массивах режимов адресации.
Адрес операнда находится в общем регистре. После определения
местоположения операнда производится автоматический инкремент
регистра и в нем образуется адрес следующего элемента массива.
56
Величина инкремента Rn зависит от типа данных операнда: 1 —
для байта, 2 — для слова, 4 — для длинного слова или числа с пла-
вающей точкой, 8 — для тетраслова или восьмибайтово^о числа с
плавающей точкой и 16 — для октаслова или шестнадцатибайто-
вого числа с плавающей точкой. (Для остальных типов данных,
например символьных цепочек, выполняется инкремент Rn на еди-
ницу, поэтому автоинкрементный режим в таких случаях нецеле-
сообразен.)
Пример 4.6. Сложение элементов массива в автоинкрементном режиме. Цикл
суммирования всех элементов массива предусматривает ввод всего двух команд —
для выполнения собственного сложения и для управления циклом. Предположим,
что адрес AMNTS начала массива длинных слов находится в регистре R10, а со-
держимое регистра R7 сброшено для загрузки в него суммы:
ADD: ADDL2 (R10)+,R7 -.Прибавить следующий элемент
(команда управления циклом, которая передает управление ADD, если цикл
не закончен)
Предположим, что команда ADDL2 выполнена несколько раз и что содержимое
регистров и памяти перед ее очередным выполнением имеет вид:
Содержимое памяти Адрес ячейки
R7 00034CD6 000102Е7 000009А4= AMNTS
R10 000009D8 FFFF8A30 000009D8 ’
00 0 0 0 671 000009DC
FFFFA09 00000В30 (конец массива)
После следующего выполнения команды ADDL2 получим:
R7
0002D706 Содержимое памяти не изменяется
R10
000009DC
Отметим, что по завершении цикла в регистре R10 будет содер-
жаться значение 00000В34 — адрес первого байта, следующего за
концом массива.
Так как инкремент Rn осуществляется после определения адреса
операнда, а не после завершения операции, при использовании
Rn в автоинкрементном режиме для определения двух операндов
в одной и той же команде операнды находятся в разных ячейках и
инкремент Rn производится дважды. Пусть, например, содержи-
мое регистра R8 составляет 000102С7. По команде
MOVB (R8) +, R(8) + ^копировать байт флагов
будет определен адрес первого операнда, равный 102С7, произведен
инкремент R8 на единицу (поскольку операнд — байт), задан адрес
второго операнда, равный 102С8, выполнен инкремент R8 на единицу
и, наконец, скопирован байт из ячейки с адресом 102С7 в ячейку с
адресом 102С8. В результате содержимое регистра R8 составит
000102С9.
Автодекрементный режим: —(Rn). Величина инкремента (1, 2,
57
4, 8 или 16, как в автоинкрементном режиме) вычитается из Rn,
а новое содержимое Rn используется затем как адрес операнда.
Автодекрементный режим применяется при «обратной» обра-
ботке массива — от элементов с большими адресами к элементам
с меньшими адресами. Особенно удобен он для сложения элементов
структуры данных, называемой стеком (см. гл. 9), а совместно с авто-
инкрементным режимом — при поиске в массивах или цепочках.
Пример 4.7. Автодекрементный режим. По команде
CLRW — (R5)
содержимое регистра R5 и слово в памяти модифицируются следующим образом:
До выполнения: Содержимое памяти Адрес ячейки
R5 95В5 000008D2
000008D4 7051 000008D4
После выполнения:
000008D2 0000 000008D2
7051 000008D4
Отметим, что в автоинкрементном режиме инкремент регистра
осуществляется после использования содержащегося в нем адреса
для локализации операнда, а в автодекрементном режиме декре-
мент регистра производится до этого момента.
4.4. Резервирование
и инициализация
областей данных
Здесь мы рассмотрим два вида ассемблерных директив для ре-
зервирования блоков памяти — с инициализацией их содержимого
на нуль и с инициализацией их содержимого на значения, опреде-
ляемые программистом.
Напомним, что ассемблер считает любые числа в исходных ас-
семблерных операторах десятичными, если программистом явно не
задана другая система счисления.
Резервирование блоков памяти. Директивы .BLKx сообщают
ассемблеру о резервировании памяти для различных типов данных
(х — указывает тип данных). Имеются следующие директивы .BLKx:
Директива Тип данных
.BLKB Байт
.BLKW Слово
.BLKL Длинное слово
.BLKQ Тетраслово
.BLKO Октаслово
.BLKA Адрес (длинное слово)
.BLKF * Число с плавающей точкой (длинное слово)
.BLKD Двойное число с плавающей точкой (тетраслово)
.BLKG 1 Необязательные дополнительные числа с плавающей точкой
.BLKH J
Формат рассматриваемых директив:
.BLKx выражение
58
Выражение определяет число резервируемых единиц памяти;
обычно им является константа или простое выражение. Любые
используемые в выражении имена должны быть заданы до появле-
ния директивы .BLKx. Существуют и другие ограничения на выра-
жения, употребляемые в ассемблерных директивах, но здесь они не
приводятся. Ассемблер заполняет всю резервируемую память ну-
лями.
Пример 4.8. Директивы резервирования памяти:
NUM - 70 1 NUM максимально» число эламантоа
AMNTSi . BLKL 100
ACCNTSi .BLKB NUM « 52
MPQi . BLKF 40
Первый оператор резервирует 100 длинных слов или 400 байт. Во втором опера-
торе предполагается, что NUM есть максимальное число элементов в массиве
ACCNTS, причем каждый элемент содержит 52 байта. Третий оператор резерви-
рует память для массива чисел с плавающей точкой MPG, состоящего из 40 эле-
ментов.
Как же все-таки следует понимать фразу о том, что ассемблер
резервирует память? Напомним, что ассемблер модифицирует свой
счетчик ячеек (который обозначается точкой) по мере обработки
исходных операторов. Поэтому резервирование памяти означает,
по существу, возрастание значения счетчика ячеек на соответст-
вующую величину.
Пример 4.9. Обработка директивы ассемблером. Предположим, что . = 9А4,
когда ассемблер встречает первый оператор из предыдущего примера. Ассемблер
регистрирует имя AMNTS в своей таблице имен и присваивает ему значение 9А4.
Затем он определяет, что резервируется блок памяти в 400 байт. Так как 400ю= 19016»
ассемблер модифицирует «.» и устанавливает значение . = .4-190= В34. При обра-
ботке следующего оператора имени ACCNTS будет присвоено значение В34.
Директива .BLKx явного резервирования памяти для символьных
цепочек или упакованных десятичных данных отсутствует. Програм-
мист вычисляет, сколько байтов потребуется для хранения таких
данных, и пользуется директивой .BLKB (или любой другой).
Пример 4.10.
LINE: .BLKB 80 .Выходная строка
Этот оператор резервирует в памяти 80 байт для символьной цепочки, содер-
жащей до 80 байт.
Инициализация целочисленных данных. Директивы инициали-
зации данных предписывают ассемблеру запоминать данные в ука-
занных областях памяти. Имя директивы определяет тип запоми-
наемых данных, а аргумент(ы) — их значения. (Если значение
слишком велико для конкретного типа данных, выдается сообщение
об ошибке.) Форматы аргументов в зависимости от типа данных
имеют некоторые различця. Рассмотрим группу директив:
59
.BYTE список_аргументов
.WORD список_аргументов
. LO NG список_аргументов
В каждой из этих директив список аргументов может содержать
один или несколько аргументов, разделяемых запятыми и представ-
ляемых в следующем формате:
данное [коэффициент повторения]
Данное и коэффициент повторения могут быть выражениями.
Ассемблер запомнит в следующих доступных ячейках такое число
копий данного, которое задано коэффициентом повторения. Данные
запоминаются в дополнительном коде. Коэффициент повторения
не обязателен. Если он не используется, его значение предполагается
равным единице. Коэффициент повторения необходимо заключить
в квадратные скобки, и любые имена в выражении должны быть
определены.
Пример 4.11. Инициализация памяти. Предположим, что . = 58А, когда ассем-
блер встречает следующие операторы:
COORDS: .WORD 2,27,-180, 492
ALPHA: .BYTE 5(3]
После обработки операторов содержимое памяти принимает вид:
Содержимое памяти Адрес ячейки
0002 0000058А
001В 0000058С
FF4C 0000058Е
01 ЕС 00000590
0505 00000592
05 00000594
Здесь определены два имени:
COORDS =58А
ALPHA =592
Окончательное значение счетчика ячеек равно 595. f
Пример 4.12. Предположим, что в примере 4.8 вместо простого резервирования
пространства для массива ACCNTS желательно инициализировать все его байты
на —1. Тогда вместо директивы .BLKB необходимо записать:
ACCNTS: .BYTE — 1 [NUM*52]
Для запоминания 8 байт данных можно воспользоваться дирек-
тивой .QUAD со следующим форматом:
.QUAD константа
ИЛИ
.QUAD имя
В директиве .QUAD допустим только один аргумент, который
не может быть общим выражением и не может иметь коэффициента
повторения. (Система VAX не поддерживает тип данных «тетра-
слово» с такой полнотой, как другие целочисленные типы данных;
для тетраслов предусмотрено меньшее число команд и менее гибкие
ассемблерные директивы.)
Инициализация символьных данных. В системе VAX для пред-
ставления символов (букв, цифр, знаков пунктуации, специальных
и управляющих символов) применяется код ASCII. Каждый символ
60
в коде ASCII содержит 7 бит и хранится в байте, причем левый бит
всегда равен 0. Список кодов ASCII приведен в приложении С (а
также в руководстве VAX-11 Programming Card)':
Символьная цепочка представляется в памяти последовательно-
стью соседних байтов — по одному символу в байте. Директива
.ASCII (и несколько ее разновидностей) сообщает ассемблеру о
запоминании символьной цепочки. С помощью этой директивы за-
даются заголовки и другие сообщения, которые выводит программа.
Мы будем пользоваться директивами .ASCII и .ASCIZ с указанны-
ми ниже форматами:
.ASCII спецификация символьной цепочки
. ASCIZ спецификация символьной_цепочки
В соответствии с директивой .ASCII цепочка запоминается в
следующих доступных байтах: первый символ цепочки находится
в первом байте (т. е. в байте с наименьшим номером) и т. д. Соглас-
но директиве .ASCIZ цепочка запоминается аналогичным образом,
но в конце ее добавляется байт с нулевым содержимым. В некото-
рых случаях такая идентификация конца цепочки оказывается
весьма удобной. В гл. 5 приводятся рекомендации по выбору ди-
ректив .ASCII и .ASCIZ.
Простейший способ определить цепочку — записать ее между
символами-ограничителями, в качестве которых часто используется
наклонная черта.
Пример 4.13. Инициализация символьной цепочки. В соответствии с директивой
TITLE: .ASCII /Daily Sales/
запоминаются следующие данные:
TITLE
44 61 69 6С 79 20 53 61 6С 65 73
Если наклонная черта есть в цепочке, ее нельзя применять как
ограничитель. В этом случае допускается любой другой символ (кро-
ме пробела, знаков табуляции и равенства, точки с запятой и левой
угловой скобки), не появляющийся в цепочке.
Управляющие символы — это коды ASCII, которые не соответст-
вуют печатному символу, а вызывают некоторое действие, например
возврат каретки. Если включить такой символ в цепочку, хранимую
в памяти, ее уже будет нельзя отпечатать, так как символ возврата
каретки завершает оператор печати. В директивах .ASCII и .ASCIZ
управляющие символы должны быть представлены их кодами ASCII.
Коды необходимо заключить в угловые скобки, находящиеся вне
ограничителей, обрамляющих остальную часть цепочки. (Символы, .
которые не являются управляющими, допускается определять та-
ким же образом, но лучше просто печатать их.)
Пример 4.14. Символьная цепочка, содержащая управляющие символы. Опе-
раторы
61
LF = 10
; Код ASCII перевода строки
HDG: . ASCIZ <LFXLF>/THIRD LINE/
заставят ассемблер заполнить следующие 13 байт такими символами:
HDG
ОА ОА 54 48 49 52 44 20 4С 49 4Е 45 00
Ассемблер присвоит значение имени HDG ячейке первого байта символьной
цепочки. При печати этой цепочки или индикации на терминале будут пропущены
две строчки (из-за наличия символов «перевода строки»), а в третьей строке появится
цепочка «THIRD LINE». Отметим, что пробел считается символом и имеет свой
код ASCII. Заметим также, что имя LF используется в директиве как поясняющее.
Пример 4.15. Дополнительные символьные цепочки
CR - 13
LF - 10
I Возврат каретки
I Перевод строки
TITLEi .ASCIZ /DAILY TRANSACTIONS/
DEPi .ASCII /PAGE 17<CRXLF>/DEP0SITS/
DATEl .ASCII *9/14/83*
Директива .ASCIZ определяет простую символьную цепочку длиной 18 байт; она
состоит из 19 байт, так как в конце ее добавлен нулевой байт. В первой директиве
.ASCII символьная цепочка разделена на несколько сегментов, заключенных в
ограничители. Длина всей цепочки равна 16 байтам. Если вывести цепочку на
терминал или строчный принтер, то в первой строке будет напечатано: «PAGE 1», а
в начале следующей строки—«DEPOSITS». В последней директиве .ASCII из-за
наличия в символьнрй цепочке наклонной черты в качестве ограничителей выбраны
апострофы.
Пример 4.16. Инициализация цепочки пробелов в памяти. В соответствии с
директивой
LINE: .BYTE 32 [80] ;80 пробелов
каждый из последующих 80 байт инициализируется на целое число 32, которое
является кодом пробела. Отметим, что, хотя мы и можем считать данные в памяти
символьными кодами, здесь директива :ASCII для заполнения байтов не
используется.
4.5. Начало и конец программы
Предположим, что требуется написать программу для вычисле-
ния BETA = 3*ALPHA—20, где ALPHA и ВЕТА—целые числа в
формате длинного слова. (При описании вычислений с помощью
имен переменных ALPHA и ВЕТА мы обозначаем содержимое ячеек
памяти с данными именами, а не их адреса, т. е. так, как это приня-
то в языках высокого уровня.) Из предыдущих примеров уже из-
вестно, каким образом можно написать команды выполнения ариф-
62
метических операций и директивы инициализации ALPHA и резер-
вирования памяти для ВЕТА. Напишем следующую программу:
|ДАННЫЕ
।
ALPHAt .LONG 224
BETA! . BLKL 1
I
I
| ПРОГРАММА ВЫЧИСЛЕНИЯ BETA - 3 * ALPHA - 20
I
l Использование регистров! R6 промежуточный результат
, I
MULL3 #3,ALPHA,R6 J R6 - 3 * ALPHA
8UBL3 #20fR6fBETA J BETA - R6 - 20
Как сообщить ЭВМ, что команды начинаются с третьего длин-
ного слова и что она не должна пытаться выполнять данные как
команды? Это может сделать только программист. Как ЭВМ узнает,
что следует прекратить выполнение команд после команды SUBL3?
И опять здесь не обойтись без программиста. (Можно было бы дого-
вориться об останове после выполнения последней команды, но в
следующей ячейке памяти всегда есть какое-то содержимое; ЭВМ
не известно, что мы считаем последней командой, если ей это спе-
циально не указать.) Для управления ассемблированием и выпол-
нением программы требуются несколько команд и директив.
Далее будут описаны стандартные операторы ассемблерных
программ системы VAX. В гл. 5 рассмотрены макрокоманды, кото-
рыми придется заменить некоторые из этих операторов при работе
с обсуждаемыми в книге макрокомандами ввода-вывода. Однако
читателям, планирующим воспользоваться макрокомандами ввода-
вывода, не рекомендуется пропускать приводимый ниже материал.
Директива .ENTRY. Одна из директив, определяющих, где начи-
нается выполнение программной секции, —.ENTRY, устанавливаю-
щая точку входа. Точкой входа называется то место в программе
(основной программе или процедуре), с которого может начаться
выполнение этой программы. Директива .ENTRY имеет следующий
формат:
.ENTRY имя, маска_входа
Здесь имя обозначает точку входа. Ассемблер присваивает имени
текущее значение счетчика ячеек. Назначение маски входа об-
суждается при изучении процедур. Пока же начинающим
программистам рекомендуется применять маску Z'M<IV>. Эта
маска входа устанавливает бит прерывания IV (целочисленное
63
переполнение) в PSW, поэтому при переполнении программа
прерывается. (Если маска входа равна 0t бит прерывания IV не
устанавливается.) Директива .ENTRY должна быть перед первой
выполняемой командой программного модуля.
Директива .END. Директива .END помещается в самом конце
исходного файла; она сообщает ассемблеру о достижении физичес-
кого конца транслируемого модуля и имеет следующий формат:
.END адрес_____перехода
Адрес перехода представляется именем, определяющим, где
должно начаться выполнение программы. Имеется различие между
адресом перехода и точкой входа. Основная программа и каждая
процедура должны иметь точку входа — точку, где начинается их
выполнение. Адрес перехода — это точка входа основной програм-
мы. Он задается в директиве .END основной программы; в дирек-
тиве .END процедуры его не должно быть, так как выполнение
программы в процедуре не начинается.
Макрокоманда $ EXIT____S. Для завершения выполнения програм-
мы допускается использовать макрокоманду $EXIT S. Ассемблер
заменяет ее последовательностью команд, которая”осуществляет
необходимые действия и передает управление операционной системе.
Законченная программа. Перепишем пример программы вычис-
ления ВЕТА, пользуясь рассмотренными выше управляющими
командами. Звездочки слева показывают вновь введенные опера-
торы; они не являются частью программы.
| ДАННЫЕ
ALPHAt .LONG 224
BETA! . BLKL 1
I
| ПРОГРАММА ВЫЧИСЛЕНИЯ BETA - 3 * ALPHA - 20
I
I Использование регистров! R6 промежуточный результат
I
* .ENTRY COMPUTE f ЛМ<IV>
MULL3 #3,ALPHA,R6 | R6 - 3 » ALPHA
SUBL3 #20,R6,ВETA | BETA - R6 - 20
* BEXIT.8
« .END COMPUTE
Теперь получилась корректная программа, которую можно ас*
семблировать и после редактирования связей выполнять.
Отметим, что в приведенной программе данные размещены до
машинных команд. При правильном разделении команд и данных
последние можно располагать как до, так и после выполняемой
64
t
части программы, однако рекомендуется помещать вначале данные
(см. гл. 8). Использование имен ALPHA, и ВЕТА в комментариях
соответствует принятому в языках высокого уровня.
Визуальное разделение частей программы обеспечивается с по-
мощью пустых строк комментариев. В программе предусмотрен
краткий комментарий о ее функции и использовании общих реги-
стров; каждая команда также содержит комментарий. Все програм-
мы надлежит документировать хотя бы в таком виде, как было по-
казано выше. Однако приведенная программа не выведет никакого
результата. Команды, предназначенные для ввода и вывода данных
будут описаны в следующей главе.
4.6. Форматы операторов
Для более четкого представления ассемблерных программ, поля
каждого оператора по возможности должны начинаться в следую-
щих позициях:,
Поле Метка Оператор Операнд Комментарий Позиция 1 9 17 41
Поля оператора и операнда необходимо разделять минимум
одним пробелом или знаком табуляции. Отдельные операнды раз-
деляются запятыми. Поле метки заканчивается двоеточием, а поле
комментария должно начинаться точкой с запятой.
Ассемблерный оператор можно записать в несколько строк,
указав дефис как последний непустой символ (до комментария,
если он есть) в каждой продолжающейся строке. Если единствен-
ным полем в первой строке является поле метки (и, возможно, ком-
ментарий), то дефис разрешается опустить. В том случае, когда
метка содержит более семи символов, фирма DEC рекомендует пе-
ренести оставшуюся часть команды на следующую строку, чтобы
оператор начинался в позиции 9. Ассемблерный оператор, занима-
ющий более одной строки, следует разделять логически, например
между полями или операндами.
Исключения из общего правила имеют два формата — операто-
ров прямого присваивания и операторов, содержащих только ком-
ментарий. Как правило, имя в операторе прямого присваивания
начинается в позиции 1 и допускается поле комментария. Точка
с запятой в операторе комментариев размещается в позиции 1.
3 За к. 821 65
4.7. Заключение
Большинство ассемблерных операторов содержит четыре поля:
поле метки (начинающееся в позиции 1), поле оператора (начинаю-
щееся в позиции 9), поле операнда (начинающееся в позиции 17)
и поле комментария (начинающееся в позиции 41).
Для обозначения (именования) данных, команд и констант
предусмотрены имена. Именам, определяемым пользователем, мо-
жет быть присвоено значение — указанием имени в поле метки или
заданием значения в операторе прямого присваивания. В первом
и более общем случае имени присваивается текущее значение счет-
чика ячеек. Следовательно, значением определенного таким образом
имени является адрес, а не данные, хранящиеся по этому адресу.
Различают три вида ассемблерных операторов: машинные коман-
ды, ассемблерные директивы и макрокоманды. Имя машинной
команды образовано мнемоникой той операции, которую она выпол-
няет, типом (ами) данных, которым она оперирует, и (иногда) числом
операндов.
В операндах машинных команд применяется позиционный про-
токол — порядок следования операндов определяет назначение каж-
дого из них. Операнды могут находиться в регистрах, в памяти и в
самой команде. Результат операции обычно помещается в послед-
ний операнд.
В системах VAX имеется большой выбор режимов адресации.
Некоторые из наиболее часто используемых режимов адресации
представлены в табл. 4.1.
Рассмотренные в настоящей главе директивы резервирования и
инициализаций памяти приведены в табл. 4.2. Ассемблер резервирует
Таблица 4.1
Некоторые режимы адресации
Название Обозначение Местонахождение операнда Особенности
Регистровый Относительный Литеральный Режим перехода Регистровый, косвенный Автоинкрементный Автодекрементн ый Rn адресное выражение # выраже- ние назначение (Rn) (Rn) + -(Rn) В регистре В памяти В команде В памяти В памяти, адрес в Rn В памяти, адрес в Rn В памяти, адрес в Rn после декре- мента Использует первый байт или слово в командах типа В или W; Rn и Rn+ 1 для Q, D или G; Rn,...,Rn + 3 для Н Инкремент Rn на размер типа данных Декремент Rn на размер типа данных
66
Таблица 4.2
Директивы резервирования и инициализации памяти
Директива Пояснение
. В L Кх выражение Выражение указывает число резервируемых единиц; х обозначает тип данных (х=В, W, L, Q, О, A, F, D,
.BYTE список .WORD список .LONG список или Н) Перечисленные значения запоминаются в последо- вательных байтах, словах или длинных словах. Можно использовать коэффициент повторения
.QUAD значение .ASCII цепочка .ASCIZ цепочка (заключенный в квадратные скобки) Значение запоминается в следующем тетраслове Запоминается символьная цепочка По директиве .ASCIZ в конце цепочки добавляется нулевой байт
память, выполняя инкремент счетчика ячеек на требуемое число
байтов. Целочисленные данные хранятся в дополнительном коде.
Символьные цепочки представляются в памяти в коде ASCII
как последовательность смежных байтов — по одному символу в
байте. В директивах .ASCII и .ASCIZ запоминаемая цепочка запи-
сывается между ограничителями. Управляющие символы допуска-
ется определять, заключая их коды в угловые скобки.
Ассемблер интерпретирует числа в операндах машинных команд
и в аргументах директив как десятичные, если программистом не
определена другая система счиЬления.
По директиве .ENTRY задается точка входа программного моду-
ля, т. е. указывается, где может начинаться выполнение этого моду-
ля. Директива .END сообщает ассемблеру о достижении конца
исходного модуля и определяет как аргумент точку входа основной
программы, показывая, где начинается выполнение всей программы.
Для окончания выполнения программы допускается использовать
макрокоманду $ЕХ1Т____S.
Данные в программе необходимо отделить от исполняемых
команд. Обычно директивы резервирования и инициализации па-
мяти размещаются в начале модуля перед директивой .ENTRY.
4.8. Упражнения
1. Напишите команду, которая присваивает значение 10 имени MARGIN.
2. Перечислите некоторые различия между счетчиком ячеек и программным
счетчиком PC.
3. Предположим, что . = 04А2, когда ассемблер встречает следующие операторы:
LENGTH: .WORD 27 Длина прямоугольника
WIDTH: .WORD 5 ;Ширина прямоугольника
Каковы значения LENGTH и WIDTH?
4. Пусть наряду с операторами из упр. 3 программа содержит оператор
AREA: .BLKW 1 ;Площадь прямоугольника
Напишите команду вычисления площади прямоугольника, длина и ширина которого
записаны в ячейках LENGTH и WIDTH; площадь поместите по адресу AREA.
5. Попытайтесь определить функции каждой из следующих команд и дайте
краткие пояснения: a) ADDD3, б) СМРВ, в) DIVL3, г) BLEQ, д) CVTBW, е) MOVQ,
ж) CLRF, з) DECW, и) MULP.
3*
67
6. Напишите команду, по которой суммируется содержимое регистров R7 и R4
как длинные слова, а сумма помещается в регистр R9.
7. Допустим, регистр R6 содержит адрес массива тетраслов. Напишите выра-
жение для адреса третьего элемента, массива.
8. Регистр R7 содержит адрес целочисленного слова. Напишите команды
пересылки целого числа в регистр R10 и загрузки адреса целого числа в регистр Rl 1.
9. Предположим, что регистры и память содержат следующие данные:
Содержимое памяти Адрес ячейки
R6 010А 096С
0000096С 39АЕ 096Е
R7 0108 ’ 0970
00000970
R9
000002 В 4
Покажите изменения, которые произойдут в памяти и регистрах после выполне-
ния команды
ADDL3 R7,(R6),R9 -.Сложить объемы продаж
10. По команде CVTLW целочисленное длинное слово (первый операнд) пре-
образуется в слово (второй операнд). Пусть регистр R5 содержит значение
00183490, а регистр R8 — 001А38Е4. Что будет с содержимым этих регистров после
выполнения команды
CVTLW (R5) + ,(R8) t ;Усечь VOLUME до слова
И. Напишите команды для'загрузки числа 17 в правый байт регистра R4 и в
байт памяти, адрес которого записан в регистре R3.
12. Предположим, что проверяется каждый символ цепочки с именем TEXT и дли-
ной 30. Просмотр ведется от ее конца к началу и для адресации символов исполь-
зуется регистр R7 в автодекрементном режиме. Какое число необходимо перво-
начально загрузить в регистр R7?
13. Напишите команды для распределения блока GRADES из 20 слов, массива
байтов CODES с 13 элементами и символьной цепочки HEADING длиной 11.
14. Напишите ассемблерные операторы для резервирования последовательно-
сти из 120
байт памяти и определите имена со следующими значениями: .
Имя Значение
LINE Местоположение первого байта
COL1 Местоположение 11-го байта
COL2 Местоположение 26-го байта
COL3 Местоположение 41-го байта
15. Допустим, что программа начинается со следующих директив:
ALPHAS .WORD 250
BETAS .LONG 5123,835
TITLEi .ASCII /Program 1/
а) Дайте значения ALPHA, BETA и TITLE.
б) Сколько байтов памяти распределяют эти директивы?
16. Пусть . — 828, когда ассемблер встречает приведенные ниже операторы.
Покажите содержимое всех областей памяти, перечислите значения всех определен-
ных имен и приведите окончательное значение счетчика ячеек.
DELTAS .WORD 24,-18,3
INDs • BLKB 2
LABELS .ASCII /VAX-11/
TAGS .BYTE 1
VOLUMES .BLKL 3
68
17. Напишите директиву для запоминания следующих символьных цепочек и
введите для них подходящие метки:
a) MPG=3.785*MILES/LITERS
б) ♦♦♦♦INVENTORY****
18. Почему предпочтительнее инициализировать 80 байт на код пробела с
помощью директивы
.BYTE 32 [80]
а не директивы .ASCII? (Как можно сделать это с помощью директивы .ASCII?)
19. Напишите законченную программу для решения следующей задачи. В па-
мяти находятся три соседних слова, начиная с ячейки NUMBRS. Предполагается,
что инициализировано только первое число. Когда,выполнение программы закан-
чивается, второе слово должно содержать удвоенное число из первого слова, а
третье — удвоенное число из второго слова.
20. Напишите команды для вычисления значения Зу2—4у+1, где у — длинное
слово, находящееся в регистре R6. Поместите результат в регистр R8.
Глава 5
Простые макрокоманды ввода-вывода
5.1. Макрокоманды
В ЭВМ ввод и вывод — это довольно сложные процессы. Боль-
шинство рутинных операций реализуется операционной системой,
но даже применение процедур операционной системы и макрокоманд
требует от читателя более глубоких знаний. В настоящей главе
обсуждаются простые в употреблении макрокоманды, которые ос-
вобождают пользователя от изучения тонкостей функций системы
ввода-вывода. Макрокоманды представляют собой псевдокоманды;
когда они встречаются в программе, ассемблер заменяет их последо-
вательностями других команд, которые выполняют требуемые опе-
рации. Описываемые ниже макрокоманды не являются утилитами
системы VAX; Прежде чем начать применять их, необходимо соз-
дать библиотеку, содержащую макроопределения из приложения
D, а модуль ввода-вывода (сведения о нем также приводятся в
приложении D) должен быть связан с программой.
Здесь мы рассмотрим четыре макрокоманды, реализующие
конкретные операции ввода-вывода: ввод строки с терминала, ввод
записи из дискового файла, вывод строки (или нескольких строк)
на терминал и вывод (дамп) на терминал шестнадцатеричных
данных. Рассмотрены также две макрокоманды, в соответствии с
которыми осуществляются необходимые служебные операции по
инициализации и завершению, относящиеся к операциям ввода-
вывода.
При реализации макрокоманд используются регистры R0 и R1,
поэтому пользователь не должен оставлять в них важные данные.
Некоторые макрокоманды возвращают программе в регистре R0
полезную информацию. (Соглашение о том, что макрокоманды мо-
гут разрушать старое содержимое регистров R0 и R1 и возвращать
в них информацию программе, распространяется и на системные
макрокоманды ввода-вывода.) Регистр SP нельзя привлекать для
определения аргументов макрокоманд.
70
Макрокоманды ввода-вывода
READLINE
Назначение этой макрокоманды — ввести строку с терминала.
Она индицирует (печатает) в качестве стимула два вопросительных
знака и пробел, а затем вводит строку текста, набираемого на кла-
виатуре терминала. Формат макрокоманды:
READLINE куда '
Цепочка введенного текста запоминается в памяти, по одному симво-
лу в байте, начиная с байта, адресуемого аргументом куда. »
Длина введенной строки равна числу напечатанных символов
(включая пробелы и знаки табуляции) до символа возврата каретки.
Макрокомандой вводится до 80 символов. Длина строки возвраща-
ется программе в регистре R0.
Пример 5.1. Считывание строки с терминала. Предположим, что программа
содержит директиву
LINE: .BLKB 30
и команду
READLINE LINE
До выполнения программы 30 байт, начиная с LINE, содержат нули. Допустим,
что при выполнении команды READLINE и появлении стимула пользователь пе-
чатает показанную ниже строку, а затем нажимает клавишу возврата каретки
(напечатанная пользователем часть строки подчеркнута, чтобы ее можно было
отличить от стимула):
??25 —8
При этом вводится строка и байты памяти, начиная с LINE, содержат следующие
данные:
LINE LINE LINE . ' . LINE LINE ... LINE
+ 1 4-2 4-8 4-9 4-29
32 35 20 20 20 20 2D 31 38 00 . ... 00
Содержимое регистра R0 составит 00000009. Так как введенная строка состоит
всего из девяти символов, остальные 21 байт останутся нулевыми/
Отметим, что в примере 5.1 байты памяти хранят коды ASCII
символов вводимой строки, они не содержат представления вводимых
чисел в дополнительном коде. В языке Ассемблера, в отличие от
языков высокого уровня, автоматическое преобразование входных
данных во внутреннее представление не производится. Преобразо-
вания между'символьным и дополнительным кодами рассматри-
ваются в гл. 6.
READRCRD
Назначение этой макрокоманды — считать запись из последо-
вательного дискового файла с именем DATA.DAT. Пока записи
допустимо воспринимать просто как строки текста. Последователь-
ный файл — это набор записей, расположенных по порядку, т. е.
первая запись, вторая запись и т. д. Когда программа начинает вы-
полняться, указатель устанавливается на первую запись в файле.
Всякий раз при выполнении макрокоманды READRCRD происходит
считывание адресуемой записи, а указатель переходит на следующую
запись. Эти действия автоматически осуществляются службой
управления записями VAX-11.
71
Если в программе используется макрокоманда READRCRD,
обрабатываемому файлу необходимо присвоить имя DATA.DAT.
(Файл на диске не изменяется; в памяти запоминается копия счи-
тываемой записи.)
Формат макрокоманды READRCRD:
READRCRD куда
Как и в макрокоманде READLINE, аргумент куда служит адресом,
по которому вводимая запись размещается в памяти. Макрокоман-
да READRCRD считывает записи длиной до 80 байт, причем длина
записи возвращается в регистр R0. (При большей длине записи
считываются только первые 80 байт.)
В том случае, когда макрокоманда READRCRD выполняется
после того,' как считаны все записи и указатель находится в конце
файла, осуществляется переход к команде с меткой EOF. Любой
программный модуль с макрокомандой READRCRD должен содер-
жать команду с меткой EOF, даже если программист не предполагает
считывать весь файл до конца.
Пример 5.2. Считывание с дискового файла. Если первая запись в файле
DATA.DAT содержит «25 —18», то в соответствии с макрокомандой READRCRD
модифицируются память и регистр R0 точно так же, как это делается по макрокоманде
READLINE (в примере 5.1).
PRINTCHRS
Эта макрокоманда индицирует на терминале цепочку символов.
Адрес первого байта цепочки должен быть задан как первый аргу-
мент. Конец цепочки указывается либо байтом, состоящим из нулей,
за последним символом, либо спецификацией длины цепочки в ка-
честве второго аргумента. Когда определяется длина, она должна
быть целым числом типа «слово». Каждый управляющий символ
в цепочке считается отдельным символом. При наличии в цепочке
нулевого байта, остальные байты не печатаются, даже если задана
большая длина.
' Форматы макрокоманды PRINTCHRS:
PRINTCHRS цепочка
PRINTCHRS цепочка, длина
Первый формат очень удобен для печати сообщений или заго-
ловков, инициализированных в памяти с помощью директивы
.ASCIZ. Он освобождает пользователя от необходимости подсчета
символов. Для аргументов допустимы любые подходящие режимы ад-
ресации.
По макрокоманде PRINTCHRS печатаются цепочки длиной до
85 байт, что достаточно для вывода полной строки и нескольких
управляющих символов. Печать всегда начинается с левой границы
новой строки.
Отметим, что макрокоманда PRINTCHRS предполагает наличие
в указанных для цепочки ячейках памяти кодов ASCII. Она не иници-
ирует печать данных в дополнительном коде; до печати программа
должна преобразовать такие данные в символьный код.
7.2
Пример 5.3. Печать заголовка. Заголовки возможно задать и напечатать сле-
дующим образом:
HDG: .ASCIZ /OUTPUT FROM JUNE 12/
PRINTCHRS HDG
Пример. 5.4. Печать сразу нескольких строк. Коды управляющих символов
возврата каретки и перевода строки разрешается включать в цепочку для иницииро-
вания их управляющих операций. Рассмотрим следующие ассемблерные операторы:
CR 13 | Код ASCII возврата каретки
LF 10 I Код ASCII перевода строки
I
PAGEi .ASCIZ /РАВЕ 1 /<CRXLF>/JUNE 12/<LFXLF>
PRINTCHRS PAGE
По макрокоманде PRINTCHRS печатается JUNE 12 после строки
PAGE 1 (причем обе начинаются с левой границы), а затем две стро-
ки пропускаются.
Пример 5.5. Чтобы подчеркнуть независимость между инициализацией сим-
вольной цепочки в памяти и ее печатью, рассмотрим три варианта примера 5.4. При
этом несущественно, одна или несколько директив .ASCIZ и .ASCII применяются
для задания печатаемой с помощью одного оператора PRINTCHRS цепочки или
используются несколько операторов PRINTCHRS. Важно лишь, что в памяти должны
находиться корректные коды ASCII, цепочка должна заканчиваться нулевым байтом
или иметь нужную длину, а операторы PRINTCHRS — определять правильные
начальные адреса.
а) Пользуясь приведенной в примере 5.4 директивой .ASCIZ, можно получить
аналогичный результат с помощью операторов
PRINTCHRS PAGE, #6
PRINTCHRS PAGE +8
так как символьный код «Л» находится в байте с адресом PAGE+ 8. В соответствии
с макрокомандой PRINTCHRS печать всегда начинается с левого края новой строки,
поэтому первые два управляющих символа не включены в печатаемую цепочку.
73
В первой макрокоманде PRINTCHRS необходимо указать длину, поскольку после
байтов <PAGE 1» не следует нулевой байт.
в) PASEi .ASCIZ /РАВЕ 1/
DATEi .ASCIZ /JUNE 12/<LFXLF>
PRINTCHRS РАВЕ
PRINTCHRS DATE
в) РАВЕt .ASCII /PAGE 1/<CRXLF>
DATEt .ASCIZ /JUNE 12/<LFXLF>
PRINTCHRS PACE
Отметим, что в третьем варианте директива .ASCIZ заменена директивой .ASCII.
Пример 5.6. Печать записи, считанной с дискового файла DATA.DAT. Необхо-
димо зарезервировать в памяти место для считываемой записи. Если ее размер
неизвестен, можно зарезервировать 80 байт, т. е. максимум для READRCRD. Так
как запись в общем случае не заканчивается нулевым байтом, для печати записи
требуется знать ее фактическую длину. Макрокоманда. READRCRD возвращает
длину в регистре R0, поэтому в качестве аргумента длины в PRINTCHRS восполь-
зуемся содержимым этого регистра:
RECORD1 . BLKB 80
READRCRD
RECORD
PRINTCHRS
RECORD,RO
Напомним* что макрокоманда PRINTCHRS может изменить содержимое ре-
гистра R0 и далее нельзя предполагать, что в нем хранится длина записи.
DUMP LONG
Назначение этой макрокоманды — вывод на печать содержи-
мого длинных слов (из памяти или регистров) в шестнадцатеричном
формате. С ее помощью можно проверить результаты вычислений,
не зная, как преобразовать дополнительный код в символьный.
Она позволяет также просмотреть внутренние представления раз-
личных типов данных и машинных команд.
По существу, макрокоманда DUMPLONG — простейшее отла-
дочное средство, так как ее легко ввести в различные точки програм-
74
мы для индикации промежуточных результатов, адресов и т. д.
Формат макрокоманды DUMPLONG:
DUMPLONG список____аргументов
Здесь список___аргументов может содержать до 12 аргументов,
разделенных запятыми и представленных в любом режиме адреса-
ции. (Поскольку DUMPLONG может изменять содержимое реги-
стров R0 и R1, при указании их в качестве аргументов выводимые
значения могут не быть первоначальным содержимым этих реги-
стров. Для печати их содержимого его следует скопировать в другие
регистры.)
При использовании макрокоманды DUMPLONG вывод всегда на-
чинается и заканчивается строкой, содержащей три звездочки, и для
каждого выводимого длинного слова печатается спецификатор
операнда. Ее выходной формат приводится в следующем примере.
Пример S.7. Формат DUMPLONG. Результат выполнения
DUMPLONG LINE,LINE+4,R10,ALPHA,R7,(R7)
будет представлен в такой форме (с произвольно выбранными данными):
««««
LINE 20203532
LINE+4 312B2020
RIO FFFFFFE5
ALPHA 0034001A
R7 0000020B
(R7) FFFFFFFC
«««
Макрокоманды инициализации и окончания. Здесь речь идет о
макрокомандах BEGIN и EXIT. Если в программе применяется лю-
бая макрокоманда ввода-вывода, то в основной программе вместо
директив .ENTRY и $ЕА1Г___S (см. гл. 4) необходимо пользоваться
макрокомандами BEGIN и EXIT соответственно (даже если ввод-
вывод осуществляется в процедурах).
Формат макрокоманды BEGIN:
BEGIN имя__точки_входа
Этот оператор заменяется директивой .ENTRY, которая опреде-
ляет имя__точки_входа и устанавливает прерывание при целочис-
ленном переполнении. Кроме того, в программу вводятся команды
для инициализации файлов ввода-вывода. Имя______точки__входа
должно появиться как аргумент директивы .END, заканчивающий
программу.
Макрокоманда окончания выполнения программы имеет вид:
EXIT
Она не имеет аргументов и просто заменяет команду $ EXIT_S.
75
Пример 5.8. Использование DUMPLONG (законченная программа).
I Эта программа вычисляет 6 * ALPHA * ВЕТА и запоминает результат в
I RESULT.Переменные ALPHA«ВЕТА и RESULT является длинными словами.
I
LF " 10 I Перевод строки
I
TITLEi .ASCIZ /Вычисления 6 « ALPHA + BETA/<LF>
ALPHA! BETAl .LONG .LONG -1 450
RESULT! • BLKL 1
1 BEGIN EXAMPLE
1 Использование pa 1 1гистров1 RS рабочий
1 PRINTCRS DUMPLONG TITLE । Печать заголовка ALPHA«ВЕТА | Исходные данйые
MULL3 *6,ALPHA«R5 । 6 « ALPHA
ADDL3 DUMPLONG EXXT RS,BETA«RESULT | Результат в RESULT RESULT | Вывод результата
.END
Программа выдает следующий результат:
Вычисление 6 * ALPHA * ВЕТА
«««
ALPHA FFFFFFFF
BETA 000001С2
«««
*#*
RESULT 000001ВС
«««
Директивы резервирования и инициализации памяти в этом и
других примерах необходимо отделять от исполняемых команд.
Целесообразно размещать их до оператора BEGIN.
Копирование символьных цепочек — команда M0VC3. Формати-
рование данных для вывода часто требует пересылки символьных
цепочек из одной области памяти в другую. Такая задача решается
с помощью команды MOVC3, имеющей следующий формат:
M0VC3 длина, источник, получатель
Длина цепочки, являющейся целочисленным словом, равна числу
76
символов, копируемых из источника в получатель. Следующие два
операнда определяют адреса первых байтов источника и получате-
ля. Цепочка-источник не изменяется (если она, конечно, не пере-
крывается с цепочкой-получателем).
Необходимо помнить, что при выполнении команды MOVC3
используются регистры R0—R5 и разрушается их первоначальное
содержимое.
Пример 5.9. Форматирование символов для вывода (законченная программа).
Предположим, что в файле DATA.DAT хранится телефонный справочник, и каждая
запись состоит из фамилии и номера телефона. Для экономии места в файле номер
телефона следует сразу за фамилией. Приводимая ниже (законченная) програм-
ма печатает весь справочник в удобном формате.
Напомним, что по макрокоманде READRCRD осуществляется переход к коман-
ду с меткой EOF при попытке считать за концом файла.
| ФОРМУЛИРОВКА ЗАДАЧИ
I
I Программа считывает телефонный справочник ио файла DATA.РАТ
I и печатает его на терминале, в удобном формате.
I
| ФОРМАТЫ ДАННЫХ
I
I Каждая запись в файле содержит фамилию ио 20 символов (допускаются
I пробелы)«после которой следует 8-оначный номер телефона. При печа-
I ти номер телефона отделяется от фамилии 10 пробелами.
I
| КОНСТАНТЫ
I TAB - 9 1 Табуляция
LF - 10 1 Перевод строки
1
| РЕЗЕРВИРОВАНИЕ И ИНИЦИАЛИЗАЦИЯ ПАМЯТИ
1
HDBi -ASCIZ <ТАВ>/ТЕЛЕ«ОННЫЙ CnPAB04HMK/<LFXLF>
LINEINl .BLKB 28 1 Входной буфер
NAMEi .BLKB 20
.BYTE 32C103 I Пробелы
PHONEI .BLKB 8 ’
1
BEBXN PRINT.DIR
1
PRINTCHRS HDB
1 Печать заголовка
READi READRCRD LINEIN I Считать ио файла
77
M0VC3 #20,LINEIN,NAME 1 Скопировать фамилию
M0VC3 #8,LINEIN * 20,PN0NE I Скопировать номер
PRINTCHRS NAME,#38 1 Напечатать строку
BRB READ I Перейти к READ
EOF1 EXIT 1 Выполнено
1 .END PRINT.DIR
Листинги программ. Теперь читатель готов к тому, чтобы начать
писать и выполнять небольшие программы, а мы кратко поясним
формат листингов программ, формируемых MACRO— ассемблером
системы VAX-11. На рис. 5.1 представлен листинг программы из
примера 5.9. Справа на листинге показана копия исходной програм-
мы с введенными слева от операторов номерами строк. Слева от
номеров строк находятся четырехразрядные числа, являющиеся
значениями счетчика ячеек для каждой строки. Наконец, с самого
левого края в большинстве случаев размещаются машинный код
или данные, относящиеся к команде в этой строке. Машинный код
следует читать справа налево, так как первый байт команды распо-
лагается рядом со значением счетчика ячеек. Здесь не показаны
команды, подставленные ассемблером вместо макрокоманд ввода-
вывода, но можно найти их длину в байтах, сравнивая значения
счетчика ячеек в строке с макрокомандой и в следующей строке.
5.2. Заключение и команды
на выполнение программ
Общие замечания. Рассмотренные выше макрокоманды не яв-
ляются системными макрокомандами VAX-11. Чтобы применять их,
необходимо создать библиотеку макрокоманд (см. приложение D)
и связать с программой модуль ввода-вывода, описанный в прило-
жении D.
Все макрокоманды могут изменять содержимое регистров R0
и R1. Регистр SP нельзя использовать для адресации аргументов
макрокоманд.
Макрокоманда
READLINE куда
инициирует считывание строки с терминала и запоминание ее в па-
мяти по адресу, определяемому аргументом куда. Стимулом этой
макрокоманды служат два вопросительных знака. Максимальное
число считываемых символов равно 80, а фактическая длина строки
возвращается в регистре R0.
Макрокоманда
READRCRD куда
78
Машинный код, данные и др. Счетчик Номер
ячеек строки
Исходные операторы
1
2
3
; ФОРМУЛИРОВКА ПРОБЛЕМЫ
00000009 0000
0000000A 0000
5
6
7
8
9
10
11
12
13
14
15
Эта программа считывает телефонный справочник
из файла DATA. DAT и печатает его на терминале
в удобном формате.
ФОРМАТЫ ДАННЫХ
Каждая запись в файле содержит 20-символьную
фамилию и далее 8-символьный номер телефона.
При печати между фамилией и номером телефона
помещаются 10 пробелов.
КОНСТАНТЫ
44 20 45 4E 4F 48 50 45 4C 45 54 09
00 ОА OA 59 52 4F 54 43 45 52 49
Z7X fOOOOO^
V7 00000047
20* 20* 20* 20* 20* 20* 20* 20' 20* 20*
00000059
ОбббГ
0000
0000
0000
000С
0017
0033
0047
0051
0059
0059
00А9
00А9
00В9
16 ТАВ=9
17 LF-10
зв---------
19
20
21
; Табуляция
; Перевод строки
РЕЗЕРВИРОВАНИЕ ПАМЯТИ И ИНИЦИАЛИЗАЦИЯ
HDG:
.ASCIZ <TAB>/TELEPHONE DI RECTORY/<LFXLF>
Рис. 5.1. Часть листинга программы:
] DO
тГТР----FF49 CF-14
FF78 CF FF55 CF 08
28 00С9
28 00D1
00D9
11 00Е7
00Е9
0119
0119
23
24
25
26
27
28
29
30
31
34
36
37
LINEIN:
NAME:
PHONE:
READ:
EOF:
.BLKB
.BLKB
.BYTE
.BLKB
BEGIN
28
20
32(10]
8
PRINT_DIR
PRINTCHRS
READRCRD
MOVC3 #20,
MOVC3 #8,
PRINTCHRS
READ
BRB
EXIT
.END
; Входной буфер
; Пробелы
HDG
LINEIN
LINEIN, NAME
LINEIN+20, PHONE
NAME#38
; Заголовок
; Элемент из файла
; Фамилия
; Номер телефона
; Напечатать
; Перейти к READ
; Выполнено
PRINT_DIR
. /— инкремент счетчика ячеек для комментариев не производится;
2— значения имен, определяемых в операторах прямого присваивания, помещаются в таблицу имен и показываются в листинге, однако в памяти
они не запоминаются; содержимое счетчика ячеек не изменяется; 3— код ASCII символов, запомненные в памяти; 4 — в директивах резервирования
памяти листинг показывает ячейку следующего доступного байта, находящегося за выделенными байтами; 5 — макрокоманда BEGIN занимает
80 байт; 6 — макрокоманда PRINTCHRS занимает 16 байт; 7— машинный код
инициирует считывание следующей записи из последовательного
дискового файла с именем DATA.DAT. Запись помещается в память,
начиная с аргумента куда. Максимальное число считываемых сим-
волов равно 80, а длина записи возвращается в регистре R0. При
попытке считать запись за концом файла осуществляется переход
к оператору с меткой EOF (в программе обязательно должен быть
такой оператор).
Макрокоманда
PRINTCHRS цепочка, длина
инициирует печать на терминале цепочки, адрес которой указан
первым аргументом. Длина (слово) необязательна и определяет мак-
симальное число выводимых символов. Допускается печать меньшего
числа символов, если цепочка заканчивается нулевым байтом.
Макрокоманда
DUMPLONG список______аргументов
инициирует вывод значений аргументов на терминал в шестнадца-
теричной системе. Она может иметь до 12 аргументов.
Макрокоманду
BEGIN имя___точки_входа
необходимо использовать вместо .ENTRY для указания точки входа
основной программы, если программа содержит любые из рассмот-
ренных выше макрокоманд ввода-вывода. По этой макрокоманде
осуществляется инициализация, необходимая для макрокоманд
ввода-вывода.
Макрокоманда
EXIT
инициирует завершающие операции с файлами ввода-вывода и за-
меняет команду $ЕХ1Т____S для окончания программы, в которой
применяются рассмотренные макрокоманды ввода-вывода.
Команды на выполнение программ. Обсуждаемые ниже команды
входят в командный язык системы VAX/VMS и печатаются после
системного стимула $.
Команда ассемблирования программы, содержащей макро-
команды ввода-вывода, имеет вид:
MACRO имя+ [каталог] IOMAC/LIB
Здесь имя представляет собой имя файла, в котором хранится
программа, а каталог — имя оглавления, содержащего файл макро-
определений IOMAC, описанный в приложении D. Тип программно-
го файла должен быть MAR; здесь его определять нельзя.
В соответствии с вариантом /LIST в команде MACRO ассемблер
создаст файл листинга:
MACRO/LIST имя+ [каталог] IOMAC/LIB
80
Ему будет присвоено имя имяХЛЗ
Команда связывания:
LINK имя, [каталог] IOMOD
Если необходимо связать несколько модулей (например, если про-
грамма содержит процедуры), формат изменяется:
LINK основная, проц!,..., процп, [каталог] IOMOD
Выполнение программы инициирует команда
RUN имя или RUN основная
Рис. 5.2. Ассемблирование, редактирование связей и выпол-
нение программы
Действия команд иллюстрируются рис. 5.2. Отметим, что ошибки
в программе можно показать на всех трех этапах: ассемблирования,
редактирования связей и выполнения.
81
5.3. Упражнения
1. Напишите команды (включая инициализацию памяти) для печати на
терминале ваших фамилии, имени и отчества.
2. Напишите законченную программу для упр. 1 и выполните ее.
3. Напишите команды (в том числе для резервирования и инициализации
памяти), инициирующие печать на терминале сообщения «Какой сегодня день?»,
ввод реакции и ее печать.
4. Напишите законченную программу для упр. 3 и выполните ее.
5. Напишите команды (в том числе для инициализации памяти), инициирую-
щие печать трех слов в следующем порядке:
FIRST
SECOND
THIRD
Слово «FIRST» должно начинаться у левого края страницы.
6. В примере 5.6 память для входной строки резервируется с помощью
директивы . BLKB, которая инициализирует резервируемые байты на нуль, а в
макрокоманде PRINTCHRS, по которой печатается введенная строка, задан аргумент
длины. Почему? Действительно, почему бы не воспользоваться нулями, запоми-
наемыми по директиве .BLKB, для окончания печати символьной цепочки?
7. Представьте себе файл с именем DATA.DAT, содержащий список фамилий,
максимальная длина которых составляет 24 символа. Напишите и выполните програм-
му для считывания списка и вывода его на терминал. Список должен иметь
соответствующий заголовок, отделенный от фамилий пустой строкой.
8. Представьте себе файл с именем DATA.DAT, который содержит фамилии
и имена в следующем формате: первой идет фамилия из 12 символов, а вторым —
имя из 10 символов. (Если реальные фамилии и имена короче, лишние позиции
заполняются пробелами). Напишите и выполните программу для считывания
записей из файла и вывода их на терминале так, чтобы имя оказалось перед
фамилией.
9. Предположим, что файл DATA.DAT содержит список фамилий и почтовых
адресов и что каждая запись имеет следующий формат:
Фамилия 20 символов
Улица 20 символов
Город 13 символов
Штат 2 символа
Индекс 5 символов
Напишите команды для считывания записи и печати фамилии и адреса в трех
строках, обеспечив минимум один пробел между городом и штатом, а также
между штатом и индексом.
10. Напишите и выполните программу, в которой для печати содержимого
регистров R0—R11 используется макрокоманда DUMPLONG. (Как показывают
результаты, программист не должен предполагать, что значения регистров сброшены,
когда программа начинает выполняться.)
11. Напишите и выполните программу, которая инициализирует длинные слова
ALPHA, ВЕТА и GAMMA на числа 25, —13 и 58 соответственно, а затем вычисляет
произведение трех чисел и печатает его с помощью макрокоманды DUMPLONG.
Глава 6
Команды манипуляций
целыми числами
6.1. Общие замечания
В настоящей главе рассматриваются команды для выполнения
арифметических операций со знаковыми целыми числами, пересылок
и преобразований данных, арифметических сдвигов и т. д. Боль-
шинство этих операций можно выполнить по одной команде для
каждого из трех основных целочисленных типов данных — байта,
слова и длинного слова. Арифметические операции с тетрасловами
непосредственно не выполняются, но и их можно запрограммировать,
пользуясь другими командами.
При использовании арифметических команд предполагается, что
данные представлены в дополнительном коде. Имеются также коман-
ды, которые интерпретируют данные как беззнаковые (неотрицатель-
ные) двоичные целые числа. Далее, в табл. 6.1, будут приведены
диапазоны целочисленных типов данных.
Все описываемые здесь команды воздействуют на биты кодов
условий в регистре PSW, чтобы зафиксировать знак результата
и показать, нет ли переполнения: Более подробно эти биты обсуж-
даются в гл. 7. (Напомним, что, если бит IV включен в маску входа
программного модуля, переполнение вызовет прерывание про-
граммы.)
Если общий регистр определен как местоположение операнда-
источника (т. е. операнда, используемого в операции) размером
в байт или слово, процессор всегда берет операнд из правого
байта (биты 7:0) или слова (биты 15:0). Оставшееся содержимое
регистра игнорируется. В том случае, когда регистр определен
как операнд-получатель байта или слова, процессор помещает ре-
зультат в правые байт или слово, а остальная часть регистра не
изменяется. (В некоторых ЭВМ знаковый бит результата расширяет-
ся в старшую часть регистра, и • результат всегда считается
32-битовым целым числом в дополнительном коде.) Команды не
изменяют операнды, кроме операнда-получателя (если, конечно,
операнд-получатель не определен и в качестве операнда-источника).
83
6.2. Арифметические команды
Для выполнения каждой из четырех арифметических операций
(сложение, вычитание, умножение и деление) и для каждого типа
данных (байт, слово и длинное слово) предусмотрена своя команда
с двумя или тремя операндами. Из приведенных ниже вариантов
можно образовать 24 команды:
Операция ч Тип данных Число операндов
ADD (сложение) В 2
SUB (вычитание) W 3
MUL (умножение) L
DIV (деление)
При выполнении любой команды результат направляется в пос-
ледний операнд. При наличии только двух операндов результат
помещается на место второго. Если в мнемонике команды опущено
число операндов, ассемблер будет считать его равным «2».
Команды ADD и MUL. Так как сложение и умножение коммута-
тивны, путаницы с функциями операндов не возникает. Проиллюст-
рируем действия команд:
ADDx2 onl, оп2
ADDx3 onl, оп2, сумма
MULx2 onl, оп2
MULx3 onl, оп2, произв
onl+оп2-*-оп2
onl + оп2-*-сумма
оп!*оп2-*-оп2
on 1 »оп2->-произв
Здесь буквой х обозначаем тип данных (В, W или L). Количество
значащих битов произведения целых чисел может быть в два раза
больше, чем у операндов, поэтому программист должен позабо-
титься о достаточно большом размере данных.
Команда SUB. В команде вычитания первый операнд вычитывает-
ся из второго. Порядок вычитания проще всего запомнить, читая
команду слева направо: вычесть onl из оп2. Схематически
команду SUB можно представить следующим образом (х=В, W
или L):
SUBx2 onl, оп2 оп2—оп!-+оп2
SUBx3 onl, оп2, разн оп2—оп!-*-разн
Команда DIV. Первый операнд в команде деления является
делителем, а второй — делимым. Имеются такие команды:
DIVx2 onl, оп2 оп2/оп1-+оп2
DIVx3 onl, оп2, части оп2/оп1-*-частн
При необходимости частное усекается так, чтобы результат был це-
лым числом. Отрицательные частные усекаются к нулю, например
результат деления —5 на 2 равен —2. Если делитель равен
нулю, возникает ошибка времени выполнения.
84
Выполнение арифметических операций. В гл. 3 мы уже рассмат-
ривали сложение целых чисел в дополнительном коде и приводили
несколько примеров. Операнды складываются как беззнаковые
двоичные числа с игнорированием переноса из старшего разряда.
Вычитание в дополнительном коде реализуется довольно просто:
одно число вычитается из второго, как если бы операнды являлись
беззнаковыми двоичными числами. Если в старшем разряде возни-
кает заем, он учитывается так, как будто слева находится еще
один бит.
Пример 6.1. Вычитание чисел в дополнительном коде
Двоичное
Подразумеваемая 1
10000000001011110
Шестнадцатеричное
Десятичное
1005Е
94
0000000100000111
1111111101010111
263
— 169
0107
FF57
Конечно, умножение и деление — более сложные операции, но
принципы их выполнения те же, что и для десятичных чисел. Мы
рассмотрим операцию умножения в наиболее общем и простом
виде, хотя фактически в процессорах применяются различные
приемы ее ускорения.
Операнды операции умножения называются множителем и мно-
жимым. Предполагается, что получатель произведения имеет .
удвоенное число разрядов и инициализирован на нуль. Если млад-
ший бит множителя равен 1, множимое прибавляется к произве-
дению. Множимое сдвигается на один бит влево (в освобождаю-
щийся справа бит помещается 0), множитель сдвигается на один бит
вправо и младший бит множителя определяет, прибавляется или
Множитель Множимое
0000000000001110 0000000000001011
--------»» Нет сложения -------------
0000000000000111 0000000000010110
Прибавить множимое: _
0000000000000011 0000000000101100
Прибавить множимое:
0000000000000001^ 0000000001011000
Прибавить множимое:
0000000000000000 0000000010110000
Нет сложения
Рис. 6.1. Умножение чисел в дополнительном коде. Пусть множитель равен 00001110,
а множимое равно 00001011 (двоичные). Отметим, что операнды расширены со
знаком. В множителе необходимо анализировать еще несколько битов, но, так как
они нулевые, результат больше не изменится. Отметим, что в произведении бит
7 равен 1, а биты 15:8 нулевые. Произведение переполнило отведенное пространство,
и байт, запоминаемой в операнде-получателе, не является арифметически правильным
результатом. Бит переполнения в PSW устанавливается. Можно проверить результат,
переводя числа в десятичную систему счисления. Множитель равен 14, а множимое —
11. Их произведение 154 находится вне диапазона знаковых целых чисел,
представимых в байте
Произведение
0000000000000000
0000000000010110
0000000001000010
0000000010011010
85
нет (сдвинутое) множимое к произведению. Процесс продолжается
до тех пор, пока не будут проверены все биты множителя. Пере-
полнение возникает, если хотя бы один бит старшей половины произ-
ведения отличается от знакового бита младшей половины. Пример
выполнения умножения представлен на рис. 6.1.
Пример 6.2. Действие команды. Предположим, что до выполнения приведен-
ных ниже команд регистры и память содержат показанные данные. Тщательно
просмотрите результаты в колонке «Действие» и убедитесь, что каждый из них
вам понятен.
Регистры Содержимое памяти Адрес ячейки
R4 401А0010 00021044= ALPHA
EFED01A2 FFFFF104 00021048 = ВЕТА
R6 123400D7 R10 00021044 27480АЗВ 0002104С = GAMMA
Команда Действие
ADDB2 #25, ALPHA ALPHA: 401A0029
ADDW2 R4, R10 R10: 000211E6
ADDL3 R4, R10 R10: FFEF11E6
ADDL2 R4, (R10) ALPHA: 400701B2
MULW2 ALPHA, R6 R6: 12340D70
DIVB3 BETA, R4, GAMMA GAMMA: 27480AE9
SUBL3 BETA, ALPHA, R6 R6: 401A0F0C
SUBB2 BETA+1, R10 R10: 00021053
Пример 6.3. Вычисление. Вычислить Зх2+ 12х—17, где х — длинное слово,
находящееся в X, и поместить результат в длинное слово Y.
I Этот сегмент вычисляет ЗхЛ2 + 12х - 17,где х ость длинно» слово в X.
I Результат оапоминается в Y.
j Испольоование регистров! RO, R1 рабочие
1 MULL3 X,X,RO $ хл2 в RO
MULL2 #3,R0 1 3x^2 в RO
MULL3 #12,X,R1 j 12 в R1
ADDL2 R1,RO I Зх2+12х в RO
SUBL3 #17,R0,Y 1 3x2+12x - 17 в Y
Сделаем несколько замечаний в отношении примера 6.3. Отметим,
что регистры R0 и R1 применяются для хранения промежуточных
результатов. По принятому в VAX соглашению именно эти регистры
используются для хранения временных данных.
Вместо того чтобы привлекать регистр R0, можно было бы
вычислить Зх2 в X по-другому:
MULL2 Х,Х
MULL2 #3,Х
86
Основной недостаток такого способа заключается в том, что перво-
начальное значение X исчезает. В соответствии с общим правилом
программирования программа не должна модифицировать входные
данные задачи (если, конечно, модификация данных не вытекает из
самой постановки задачи, например перестройка списка для разме-
щения элементов по порядку). Введение этого правила связано с тем,
что часто программы, написанные одним программистом, являются
частями больших программ, создаваемых целым коллективом прог-
раммистов, и данные могут потребоваться в других программах.
На размещение промежуточных результатов в регистрах уходит
меньше времени, чем на запоминание их в памяти. Вместо R0 или
R1 можно было бы использовать ячейку Y, но при этом возросло
бы число обращений к памяти. Кроме того, как будет, показано в
гл. 8, сама команда оказывается короче, если ее операнды находятся
в регистрах.
Пример 6.4. Вычислить выражение R7«- R44+R3*R6, если регистры содержат
целые числа в формате слова. Отметим, что более строго следовало бы записать
(R4)4 + (R3)*(R6), но для краткости мы опускаем скобки.
MULLW3 R4,R4,R7 | R4y42 в R7
MULLW3 R3,R6,R0 J R4~4 в R7
MULLW3 R3,R6,R0 | R3 * R6 в RO
ADDW2 R0fR7 | R4~4+R3»R6 в R7
Пример 6.5. В этом и многих последующих примерах предполагается, что
обрабатываемые данные соответственно инициализированы.
I Длинное слово TOTAL содержит общую стоимость оакаоа*на который
I распространяется скидка 20%. Вычисляется сумма платежа и оапо-
I минается в длинном слове AMNT.
I
I Денежные суммы представлены в центах.
I Использование регистров! R7 величина скидки
I
DIVL3 #5,T0TAL,R7 | Вычислить скидку
SUBL3 R7,TOTAL,AMNT | Вычесть скидку
Некоторые специальные команды. Существует несколько разно-
видностей арифметических операций, которые выполняются столь
часто, что для них целесообразно предусмотреть специальные коман-
ды, реализующие эти операции быстрее и занимающие в памяти мень-
ше места. Примером такой часто выполняемой операции служит
установка регистра или ячейки памяти в нуль. В системе VAX имеют-
ся специальные команды для этой функции, а также для инкремента
(прибавления 1), декремента, (вычитания 1) и изменения знака.
87
Указанные операции можно, конечно, реализовать с помощью
команд MOV, ADD, SUB и MUL, выбрав соответствующие
операнды. Однако специальные команды CLR (сброс), INC
(инкремент), DEC (декремент) и MNEG (пересылка с изменением
знака) более эффективны и при необходимости следует применять
именно их. В команды MNEG входят два операнда: значение пер-
вого операнда (источника) с измененным знаком передается во
второй операнд (получатель). Форматы рассматриваемых команд:
CLRx получатель
INCx операнд
DECx операнд
MNEGx источник, получатель
х=В, W, L, Q или О
х=В, W, L
х=В, W, L
х=В, W, L
(Напомним, что команды CLRQ и CLRO с операндом Rn будут
сбрасывать содержимое Rn, Rn+1 и Rn, Rn+1, Rn+2, Rn + 3 соот-
ветственно и что CLRO имеется не во всех моделях VAX.)
Пример 6.6. Действие команды. Предположим, что до выполнения каждой из
приведенных ниже команд содержимое регистра R8 составляет 00009AFF.
Команда (R8) после выполнения
CLRL R8 ОООООООО
CLRB R8 00009А00
INCВ R8 00009А00
INCW R8 00009В00
DECB R8 00009AFE
DECL R8 00009AFE
MNEGL R8 ,R8 jFFFF6501
MNEGW R8 ,R8 00006501
MNEGB R8 ,R8 00009АО1
Пример 6.7. Вычисление
1 Вычислить среднее SCORE1, SC0RE2 и SC0RE3 (длинные слова)|
1 оапомнить округленный результат в AVBCR.
ADDL3 SCORE1y8C0RE2yR0 | Сложить две первых оценки
ADDL2 SC0RE3yR0 1 Прибавить третью
INCL RO 1 Прибавить 1 для округления
DIVL3 *3yR0,AVSCR 1 Найти среднее
6.3. Простая команда цикла
и адресация массива
В системе VAX предусмотрено несколько специальных команд
управления циклом. Эти команды и команды условных переходов
рассматриваются в гл. 8, но одну простую команду цикла мы приве-
дем уже здесь, чтобы читатель мог писать и выполнять нетривиальные
88
программы. Речь идет о команде SOBGTR — вычесть единицу и
выполнить переход, если результат больше нуля, формат которой
имеет вид:
SOBGTR индекс, назначение
Индекс с форматом длинного слова может находиться в регистре
или памяти. Команда выполняется таким образом
индекс индекс—1
если индекс >0, то перейти по назначению,
иначе выполнять следующую команду
Приведенная ниже схема показывает обычное применение коман-
ды SOBGTR:
индекс число повторений цикла
LOOP:
{команды цикла)
SOBGTR индекс, LOOP
При таком использовании команды индекс всегда означает,
сколько раз еще цикл будет повторяться; когда индекс равен
нулю, управление передается команде, следующей за циклом.
Пример 6.8. Программирование с массивом
Проблема. Напишите сегмент программы для удвоения каждого элемента в
массиве DATA слов. Число элементов массива указано в длинном слове NUM.
Обсуждение возможного решения. Решение довольно очевидно. Воспользуемся
регистром, например R6, для адресации каждого элемента массива по очереди.
Переменная (или регистр), которая содержит адрес, обычно, называется указателем,
поэтому будем называть R6 указателем массива.
Здесь удобен автоинкрементный режим адресации; после удвоения элемента
содержимое R6 автоматически увеличивается и образует адрес следующего элемента.
Трудность заключается в инициализации указателя массива — как загрузить
в R6 адрес первого элемента массива. Воспользуемся для этого командой MOVAW —
переслать .адрес слова. При ее выполнении адрес первого операнда (? не содержи-
мое по этому адресу) загружается во второй операнд. Команды TVIOVA более подроб-
но рассматриваются в следующем разделе.
РешениеI
) ФОРМУЛИРОВКА ЗАДАЧИ
I
I Этот программный сегмент удваивает каждый элемент в 'массиве DATA (сло-
1 ва)• Число элементов массива находится в NUM (длинное слово)•
I
I Испольоование регистров! R6 указатель массива
I R9 индекс цикла
I
I Инициализация
I
MOVAW DATAyR6 | Указатель массива
89
MOVL NUM,R9 । Индекс цикла равен числу элементов
I
I Цикл
I
DOUBLEв ADDW2 (R6)y(R6)+ । Удвоение элемента и инкремент указателя
SOBGTR R9,DOUBLE j Управление циклом
Примечание. Отметим, что адрес второго операнда в команде ADDW2 вычисляет-
ся до инкремента R6 и что он используется как адрес второго слагаемого и
как адрес суммы.
Приведенный сегмент содержит больше строк комментариев, чем
команд. Такая ситуация не является необычной. Документирование
программ представляется весьма важным особенно для ассемблер-
ных программ (по сравнению о самодокументирующимися прог-
раммами на языках высокого уровня), так как в них труднее
разбираться. В начале сегмента должны размещаться формули-
ровка проблемы (задачи), типы используемых данных, описание
метода с акцентом на необычные ситуации, список и назначение
используемых регистров. В рассмотренном примере метод решения
проблемы не дается, поскольку он очевиден.
Почти каждая команда в программе должна сопровождаться
кратким комментарием, поясняющим функцию команды в терминах
решаемой проблемы. Если, например, к команде
MULL3 R7, R9, R10 ;плата = часы X тариф
привести комментарий: «загрузить в R10 произведение R7 и R9»,
он будет почти бесполезным.
Отметим наличие пустых строк комментариев для разделения сек-
ций документирования и команд. Они делают программу понятной
и поэтому их применение целесообразно.
Пример 6.9. Программирование с массивом
Проблема. Напишите сегмент программы, который запоминает в каждом элементе
массива LIST длинных слов номера индекса элемента (начиная с 1). Размер массива
содержится в длинном слове NUM.
Обсуждение возможного решения. Индекс для управления циклом в команде
SOBGTR можно использовать так же, как значение, запоминаемое в элементах
.массива. Но команда SOBGTR отсчитывает значения индекса от максимального
до нуля, и массив будет заполнен в обратном порядке. Следовательно, адресацию
элементов массива удобно осуществлять в автодекрементном режиме. Так как в этом
режиме декремент адреса производится до его использования, необходимо инициа-
лизировать указатель массива на адрес ячейки сразу после конца массива. Каждый
элемент занимает 4 байта, поэтому адрес последнего элемента равен LIST+4*(NUM).
Такую ситуацию можно проиллюстрировать рис. 6.2.
Решами»в
) ФОРМУЛИРОВКА ЗАДАЧИ
I
I Эта программа еапоминает в каждом элементе массива LIST длинных слов
I номер индекса элемента. Раемер массива находите» в длинном слове NUM.
90
I
I МЕТОД
I
1 Массив заполняется от конца к началу«поэтому индекс цикла можно 1 использовать и как значение индекса массива.
1
1 Использование регистров! RO рабочий
1 R6 индекс цикла
1 R1O указатель массива
I
I Инициализация
MOVL NUM,R6 । Индекс цикла равен числу элементов
MOVAL LIBT,R1O । Инициализировать указатель массива
MULL3 #4,R6,R0 J 4 # (NUM) в RO
ADDL2 RO,RIO | LIST + 4 * (NUM)
1
1 Цикл
1
STORE! MOVL R6«-(R10) | Запоминание индекса«декремент указателя
SOBGTR R6«STORE | Управление циклом
Рассмотренный пример позволяет сделать несколько важных вы-
водов о циклах, массивах и некоторых командах.
Индекс, применяемый для управления циклом, допускается
использовать в других командах этого цикла. Но нужно тщательно
следить за тем, чтобы индекс не модифицировала никакая другая
команда, кроме команды управления циклом, чем обеспечивается
правильное число повторений цикла.
А что если (NUM)=0? Что будет, если массив пустой? В ячейке
LIST (первый элемент массива)
LIST + 4 (второй элемент)
LIST + 8 (третий элемент)
LIST + 4 * ((NUM) - 1) (последний элемент)
LIST+4* (NUM)
Рис. 6.2. Адреса элементов массива.
Так инициализируется указатель адреса
в упр. 6.9
91
LIST следует запомнить длинное слово 0. Обычно требуется прове-
рять, не является ли массив пустым. Мы вернемся к этому вопросу
в гл. 7, где обсуждаются общие проблемы, связанные с циклами,
которые иногда должны выполняться нуль раз.
Вычисление начального значения указателя массива потребовало
выполнения нескольких команд. В операндах допускаются выраже-
ния, поэтому неопытный программист может попытаться написать
команду
MOVAL LIST+4*NUM, R10
Это некорректно, так как для ассемблера NUM— адрес, по кото-
рому хранится данное .с именем NUM. Данное (т. е. содержимое
NUM) должно быть добавлено во время выполнения с помощью
команды ADDL2. Значение данного в процессе ассемблирования
неизвестно; оно может быть получено при вводе или вычислено
другой секцией программы.
Если желательно запрограммировать вычисление адреса послед-
него элемента массива, нужно воспользоваться следующими
командами:
MOVAL LIST - 4.R10
MULL3 #4,NUM,RO
ADDL2 RO,RIO
Массив, имеющий г строк
ис столбцов
аг1 аГ2 • • • аГ/ С-! Яге
Хранение массива
в памяти
а11 а12 • • • Ч с-1 а1с
а?1 а22 * * ’ а2, с—1а2с
Рис. 6.3. Хранение двумерных массивов
92
Значение выражения LIST — 4 ассемблер может вычислить, так что
команда SUB для вычитания числа 4 во время выполнения не тре-
буется.
Благодаря многообразию команд управления циклами и режимов
адресации системы VAX отмеченную выше проблему можно решить
несколькими способами. Некоторые из них приводятся в упраж-
нениях и в следующих разделах.
Пример 6.10. Двумерные массивы
Проблема. Вычислить след квадратного массива (матрицы) длинных слов
(следом называется сумма элементов, находящихся на главной диагонали).
Обсуждение возможного решения. Двумерные массивы обычно запоминаются
по строкам, т. е. первый элемент второй строки хранится в памяти сразу за
последним элементом» первой строки и т. д. (рис. 6.3). (Компиляторы Фортрана
рассчитаны на хранение двумерных массивов по столбцам, но этот способ не является
общепринятым.) Следовательно, диагональные элементы следуют через (л-М)
элемент или 4*(л-|-1) байт.
Решение. Предполагается, что имя массива — MATRIX, длинное слово N
содержит его размер и след запоминается в длинном слове TRACE.
1 $ 1 1 Использование регистровi R6 адрес диагонального элемента R7 индекс цикла R8 след ' R9 4 * (п ♦ 1), инкремент адреса
1 MOVAL MATRIX,R6 MOVL N,R7 1 Укаоатель на первый элемент 1 Инициалиоировать индекс цикла
1 CLRL RS ADDL3 #1,R7,R9 MULL2 #4,R9 1 Сбросить след 1 1 п ♦ 1 1 4 * (п + 1)
ADDi ADDL2 (R6),R8 ADDL2 R9,R6 SOBGTR R8,ADD MOVL RS,TRACE 1 Прибавить диагональный элемент 1 Инкремент указателя 1 Управление циклом 1 Запомнить след
6.4. Пересылки и преобразования
Команды пересылок. С помощью команд MOV (переслать) пер-
вый операнд копируется во второй. Общий формат этих команд:
MOVx источник, получатель
Здесь х = В, W, L, Q или О. Как показано выше (и далее специаль- „
но не оговаривается), при задании регистрового режима и типа
данных В или W копируются либо модифицируются только правый
байт или слово регистра. Если в командах MOVQ и MOVO в ка-
честве операнда указан регистр Rn, фактический операнд занимает
два или четыре регистра соответственно. Команды MOV, как и
93
арифметические команды, устанавливают биты кодов условий в
PSW, фиксируя знак данного; их можно проверить командами
условных переходов.
Операнды (источник и получатель) в команде MOV должны быть
одного и того же типа. Иногда программист может сокращать или
расширять целое чис^о. Преобразования различных типов знаковых
целых чисел осуществляются с помощью команды CVT (преобразо-
вать). Преобразование знакового целого числа в число большего
размера производится путем расширения знака, т. е. копирования
знакового бита во все новые старшие разряды операнда-получателя.
Для преобразования длинного представления в более короткое дос-
таточно отбросить левые биты. Конечно, может оказаться, что
операнд-источник не помещается в меньший операнд-получатель;
в этом случае устанавливается код условия переполнения (и возни-
кает прерывание программы, если в маске входа определен бит
IV). Преобразование длинного представления в более короткое счи-
тается успешным, если все отбрасываемые биты идентичны знако-
вому биту результата.
Команды CVT имеют следующий формат:
CVT источник, получатель х=В, W или L
£/=В, W или L
х не равен у
Пример 6.11. Преобразования целых чисел. Предположим, что до выполнения
каждой из приведенных ниже команд содержимое регистра R5 составляет FFE729A0.
Команда
CVTBW R5.R5
CVTWL R5.R5
CVTBL R5.R5
Действие на R5
FFE7FFA0
000029А0
FFFFFFAO
Пример 6.12. Предположим, что значение регистра R6 равно 000000В4. По команде
CVTLW R6, (R8)
слово 00В4 будет загружено в'слово, адрес которого находится в регистре R8.
По команде
CVTWB R6, (R8)
В4 загрузится в байт, адрес которого находится в регистре R8. Одна-
ко последнее преобразование считается неправильным, так как слово 00В4 положи-
тельно, а байт В4 интерпретируется как целое отрицательное число в дополнитель-
ном коде.
Пример 6.13. Необходимо вычислить
10*число +цифра, •
где «число» — длинное слово в регистре R9, а «цифра» — байт в памяти, адрес которо-
го находится в регистре R10. Требуется заменить текущее значение числа на резуль-
тат и произвести инкремент регистра R10, чтобы он адресовал следующий байт
в памяти. Поставленная задача решается с помощью следующего фрагмента:
94
MULL2 #10,R9 j 10 » число
CVTBL (R10)+,R0 । Прообразовать цифру в Длинное слово
ADDL2 R0,R9 j Прибавить цифру
Почему в этом примере потребовалось преобразование? Пред-
положим, что 4 байта с адреса, первоначально находящегося
в регистре R10, содержат 04, 02, 01 и 07 (шестнадцатеричные
значения). Необходимо прибавить 04 к содержимому R9; если же
для прибавления цифры воспользоваться командой ADDL, будет
прибавлено длинное слово 07010204.
Команды преобразований беззнаковых целых чисел рассмотрены
в упражнениях, приведенных в конце главы.
Команды пересылки адреса. Команда MOVAL уже встречалась
нам в примерах разд. 6.3. для передачи адреса массива в регистр.
Задача копирования адресов возникает довольно часто. Команды
пересылки адреса имеют следующий формат:
MOV Ах источник, получатель
Здесь х=В, W, L, Q или О. В операнд-получатель копируется
адрес первого операнда. Обращения к данным, адресуемым первым
операндом, не производится, и они не модифицируются. Адреса имеют
длину 32 бита, поэтому получатель должен быть длинным словом
или регистром.
Пример 6.14. Загрузка адреса в регистр. Предположим, что FLAG — это адрес
однобайтного флага в памяти и что в байте записано 08. Пусть содержимое регистра
R7 равно FFFFFE(i7. После выполнения команды
MOVAB FLAG, R7
изменится только значение в регистре R7; он будет содержать адрес флага, но не
сам флаг.
Зачем введены различные команды MOV А для каждого типа дан-
ных, если все они пересылают 32-битовый адрес? Причина кроется
в некоторых дополнительных функциях команд. Напомним, что в
автоинкрементном и автодекрементном режимах адресации произ-
водится инкремент или декремент используемого для адресации
операнда регистра на число байтов в типе данных операнда.
Команда в формате
MOV Ах (R/i)+,Rm
инициирует инкремент регистра Rn на 1, 2, 4, 8 или 16, если х есть
В, W, L, Q или О соответственно.
Пример 6.15. Программирование с использованием команд CVTxi/ и MOV Ах.
Проблема. Сложить все элементы массива BLOCKS байтов, зная, что сумма
слишком велика для размещения ее в байте. Число элементов массива хранится
в NUM, а сумму необходимо запомнить в слове TOTAL.
Обсуждение возможного решения. Каждый элемент массива до суммирования
преобразуется в слово, а собственно сложение выполняется по команде ADDW.
Для выполнения команды SOBGTR требуется индекс размером в длинное слово,
95
поэтому размер массива (байт) следует преобразовать в длинное слово. Сумма накап-
ливается в регистре, а не в ячейке TOTAL, так как команда сложения в цикле
выполняется многократно, а доступ к регистрам осуществляется быстрее, чем в
памяти.
Решение.
I ФОРМУЛИРОВКА ЗАДАЧИ
I
I Этот программный сегмент суммирует элементы байтного массива BLOCKS.
I Так как* сумма может не поместиться в байте, элементы до сложения пре-
I образуются в слова. Число элементов находится в байте NUM* а сумма за-
I поминается в слове TOTAL.
I 9 Использование регистров! R6 указатель массива
1 1 1 9 । 5 9 1 Инициализация MDVAB CVTBL CLRW Цикл сложения 1 BLOCKS,R6 * NUM,RS R9 1 1 1 R7 преобразованный элемент R8 индекс цйкла R9 сумма Адрес массива Индекс цикла Сбросить для суммы
NEXT! CVTBW ADDW2 SOBGTR MOVW (R6)+,R7 R7,R9 RS,NEXT R9,T0TAL 1 1 1 1 Взять и преобразовать элемент Прибавить элемент Управление циклом Запомнить сумму
Пример 6.16. Считывание и запоминание целого файла
Проблема. Дисковый файл DATA DAT содержит список фамилий длиной
20 символов каждая (некоторые из символов могут быть пробелами). В памяти
резервируется область NAMES для 50 фамилий. Необходимо считать список в
память, вычислив фактическое число фамилий в файле, и вывести этот список
на терминал. Число считанных фамилий нужно запомнить в длинном слове COUNT.
Обсуждение возможного решения. При достижении конца файла макрокоманда
READRCRD автоматически переходит к команде с меткой EOF. Следовательно,
когда встретится конец файла, произойдет выход из цикла. Но, поскольку файл
может содержать более 50 записей, необходимо явно обеспечить считывание не
более 50 записей; в противном случае будут испорчены команды и (или) данные.
Решения!
I Использование регистровi
I Инициализация
R6 указатель массива
R7 счетчик цикла
96
MOVAB NAMES,R6 MOVL #50,R7 '। Инициализировать указатель массива
5 Считать максимум 50 записей
READ: READRCRD (R6) 1 Считать следующую фамилию
PRINTCHRS (R6),#20 1 Напечатать фамилию
ADDL2 #20,R6 I Инкремент адреса
SOBSTR R7,READ 1 Управление циклом
EOF: SUBL3 R7,#50,COUNT 1 Найти и запомнить COUNT
Пересылка символьных цепочек. Команда MOVC3 (переслать
символы, 3 операнда) рассматривалась в разд. 5.1. Читателям,
которые пропустили гл. 5, рекомендуется вернуться назад и прочи-
тать описание этой команды.
6.5. Преобразования
символьного
и дополнительного кодов
Проблема. Когда ассемблерная программа вводит данные, они
считываются как последовательность символов и запоминаются в
памяти в символьном коде, по одному символу в байте. Если данные
являются целыми числами, которые участвуют в сложных вычисле-
ниях, или если необходимо хранить большой объем целочислен-
ных данных, символьный код следует преобразовать в дополнитель-
ный. В дополнительном коде операции выполняются быстрее и для
хранения данных требуется меньший объем памяти. Во многих
ЭВМ, включая и машины семейства VAX, предусмотрен про-
межуточный тип данных, называемый упакованным десятичным.
Упакованные десятичные данные компактнее данных в символьном
коде, но не столь компактны как в дополнительном. Существуют
команды, инициирующие арифметические операции над упакован-
ными десятичными целыми числами, но они выполняются медленее
арифметических команд, оперирующих данными в дополнитель-
ном коде.
Преобразования между двумя представлениями данных не явля-
ются тривиальной задачей. Без мощных команд, имеющихся в
некоторых современных больших ЭВМ, такие преобразования тре-
буют значительного объема работы и времени на размышления
о том, как решить эту задачу. В настоящем разделе рассмотрены
команды VAX, с помощью которых осуществляются преобразо-
вания. В гл. 7 дается более глубокий анализ преобразований и
4 Зак. 821 97
а) Символьный код
первый байт
знак цифра цифра цифра
Коды символов
0-31 цйфр
б) Упакованное десятичное число
первый байт
d d d d d d d s
d -цифра (0-9)
s-знак (А, С, Е, F для +,
В, D для -)
Рис. 6.4. Форматы символьного кода и упакованных десятичных
чисел
показывается, как их выполнить на ЭВМ, не имеющих соответствую-
щих специальных команд.
Символьный код чисел и упакованный десятичный формат. В
системе VAX предусмотрен формат данных, называемый символь-
ным кодом чисел (leading separate numeric — Isn), -который обозна-
чает просто знаковое целое число в символьном коде (максимум
31 разряд или цифра). Знаком может быть <-Ь», «—» или пробел.
Символьные коды занимают смежные байты памяти, а знак — байт
с наименьшим адресом. На диаграммах таких данных байт с
наименьшим адресом размещается слева, чтобы символы читались
в естественном порядке: слева направо. На рис. 6.4,а представлен
формат цепочки — символьного кода числа. Адресом цепочки явля-
ется адрес знакового байта, т. е. байта с наименьшим- адресом.
Пример 6.17. Цепочка символьного кода числа. Для любого символа с, запись
’с’ обозначает шестнадцатеричный символьный код. Отметим, что символьные коды
цифр находятся в диапазоне 3016 —3916.
Первый байт Первый байт
”1’ ,2, ,0, ,4, =2D 31 32 30 34
В упакованном десятичном формате цифры «упакованы» по две
в байте. Возможность такой упаковки объясняется тем, что двоичное
значение каждой десятичной цифры требует 4 бита (0000—1001).
Знак целого числа находится в правой половине последнего бай-
та (в байте с младшей цифрой числа). Так как для цифр исполь-
зуются наборы 0000—1001, для представления знаков остаются на-
боры 1010—1111 (шестнадцатеричные цифры А — F). Знаки с их
помощью кодируются следующим образом: А, С, Е и F представляют
«+», В и D —« —». В командах преобразования данных в упако-
ванный десятичный формат применяются коды С и D.
Упакованное десятичное число может иметь максимум 31 цифру.
Его адресом считается адрес байта с наименьшим номером. Как и
для символьного кода, на диаграммах байт с наименьшим адресом
показывается слева, чтобы число можно было читать в естествен-
ном порядке слева направо. Упакованный десятичный формат
представлен на рис. 6.4,6.
98
Пример 6.18. Упакованное десятичное число
Первый байт
01 20 4D Число равно —1204
Преобразование символьного кода в дополнительный. В системе
VAX предусмотрены команды преобразования символьного кода в
упакованный десятичный формат и упакованного десятичного числа
в дополнительный код (длинное слово), а также две команды обрат-
ных преобразований. Сначала рассмотрим преобразования, которые
осуществляются при вводе — из символьного кода в дополнительный.
По команде CVTSP символьный код преобразуется в упакованное
десятичное число. Она имеет следующий формат1:
CVTSP чцск, ск, чцупк, упк
Первый операнд определяет число цифр (без учета знака) в
цепочке — символьном коде, второй — адрес цепочки, т. е. адрес бай-
та, содержащего знак, третий — число цифр (а не число байтов),
помещаемых в упакованный десятичный результат, а последний —
адрес для размещения результата. Если заданное для упакованного
десятичного результата число цифр превышает значение числа,
указанного в качестве источника, левые разряды заполняются
нулями. (Если же определено слишком мало цифр, результат усекает-
ся, возможно, с получением неправильного значения.)
Следует отметить, что команда CVTSP (и другие команды
преобразований, рассматриваемые в настоящем разделе) использует
регистры R0 — R3 в качестве рабочих, разрушая их первоначаль-
ное содержимое.
Команды CVT нужно применять осторожно, так как с ними
связано много причин для появления ошибок. Операндом-источни-
ком должна быть цепочка символьного кода. Иными словами, первый
байт должен содержать символьный код « + », «—» или пробела,
а каждый из последующих байтов — символьный код цифры. Если
источник имеет «плохой» байт (т. е. код не цифры) или если опреде-
ленное для любого операнда число цифр находится вне диапазона
0—31, возникает особый случай — так называемая ошибка зарезер-
вированного операнда (обычно она ведет к прекращению выпол-
нения программы). Если операнды перекрываются, результат дейст-
вия команды непредсказуем; другими словами, цепочку символьного
кода нельзя преобразовать в упакованное десятичное число «на
месте».
Пример 6.19. Преобразование символьного кода в упакованный десятичный.
Предположим, что по адресу DATUM размещаются следующие байты:
DATUM
2D 31 32 30 34
1 Здесь чцск— число цифр символьного кода, ск — символьный код, чцупк —
число цифр в упакованном десятичном формате, упк — упакованное десятичное
число.— Примеч. пер.
4*
99
По команде
CVTSP #4, DATUM, #5, PKD
будут сформированы слудющие байты, начинающиеся в PKD:
PKD
01 20 4D
Отметим, что упакованное десятичное число всегда имеет не-
четное число цифр. Если бы программист определил #4 как третий
операнд команды CVTSP в примере 6.19, то все равно были бы
заполнены целиком три байта (пятью цифрами).
С помощью команды CVTPL упакованное десятичное число
преобразуется в целое, представленное длинным словом в допол-
нительном коде. Формат этой команды:
CVTPL уц, упк, длинное
Здесь первые два операнда определяют число цифр (а не чис-
ло байтов) и адрес упакованного десятичного данного, а третий —
длинное слово-получатель.
Приведенное выше замечание по команде CVTSP относится и
к команде CVTPL: регистры R0 — R3 используются как рабочие
(но их можно указывать в качестве операндов-получателей, так
как результат запоминается последним), число цифр должно быть в
диапазоне 0—31, а операнд-источник должен иметь упакованный
десятичный формат. При выполнении команды CVTPL возможно
переполнение, так как наибольшее целое число в длинном слове
включает только 10 цифр (с учетом дополнительного кода).
Команда CVTPL выполняется правильно, даже если операнды
перекрываются.
Пример 6.20. Преобразование упакованного десятичного числа в число в допол-
нительном коде. Если воспользоваться данными из предыдущего примера, то
по команде
CVTPL # 5, PKD, R7
в регистр R7 будет загружено число —1204, представленное в дополнитель-
ном коде.
Пример 6.21. Программа считывания и преобразования данных:
| ФОРМУЛИРОВКА ЗАДАЧИ
I
I Программа считывает с терминала строку, которая содержит шесть 4-рао-
I рядных опаковых целых чисел. Числа преобразуются в дополнительный код
I и (запоминаются в массиве NUMBERS слов«Предполагается следующий формат
I целых чисел в строке!
I
I sddddbbbsddddbbbsddddbbbsddddbbbsddddbbbsdddd
I
100
s ® знак ( + , - или пробел)
d = цифра (могут быть старшие нули),
Ь пробел
NUMBERS I . BLKW 6
LINEi .BLKB 100
PKDi .BLKB 3
1 BEGIN CONVERT
I Испольоованиз РШГИСТРОВ1 R6 указатель массива
R7 указатель преобразуемой цепочки символьного кода
R8 индекс цикла
R9 рабочий
MOV AW NUMBERS,R6 1 Инициализировать указатель массива
MOVAB LINE.R7 1 Инициализировать указатель цепочки
MOVL #6,R8 1 Инициализировать счетчик цикла
READLINE LINE 1 Считать строку
5 CVTt CVT8P «4,(R7),64, PKD 1 Преобразовать в упакованное
CVTPL #4,PKD,R9 1 Преобразовать в длинное слово
CVTLW R9,(R6) + 1 Преобразовать в слово и запомнить
ADDL2 #8,R7 1 Инкремент указателя цепочки
SOBOTR R8,CVT 1 Управление циклом
I
I <команды обработки данных>
5
EXIT
.END CONVERT
Примечания. Жесткий формат входа значительно упрощает
для программиста задачу поиска и преобразования входных данных.
Однако пользователю программы было бы удобнее печатать вход-
ные данные в менее жестком формате (например, не заполняя
нулями старшие разряды чисел, имеющих менее четырех цифр).
Для обработки входных данных 'в свободном формате можно
воспользоваться командами проверки и зацикливания, которые рас-
сматриваются в гл. 7.
Преобразование дополнительного кода в символьный. Для печа-
ти целочисленных данных с внутренним представлением в допол-
нительном коде, рассмотренный выше процесс преобразования вы-
101
полняется в обратном порядке: сначала дополнительный код преоб-
разуется в упакованный десятичный формат, а затем результат
преобразуется в символьный код. Для таких преобразований
предназначены команды CVTLP и CVTPS.
По команде
CVTLP длинное, чцупк, упк
длинное слово (первый операнд), представленное в дополнитель-
ном коде, преобразуется в упакованное десятичное число. Второй
и третий операнды определяют число цифр, отводимых для упакован-
ного результата, и адрес, по которому он запоминается.
Команда
CVTPS чцупк, упк, чцск, ск
позволяет представить упакованное десятичное число в символь-
ном коде. (Источник и получатель не должны перекрываться.)
При использовании команды CVTPS в символьный код всегда
помещаются знаки « + » или «—», т. е. знак пробела для положи-
тельного числа не предусмотрен.
Если число значащих цифр в операнде-источнике меньше числа,
определенного для получателя, при использовании любой из этих
двух команд старшие разряды заполняются нулями. Если же
фактическое число цифр больше, чем определено для получателя,
возникает переполнение, и запоминаемый результат не может быть
точным.
Пример. 6.22. Преобразование числа и печать результата. Предположим, что
регистр R4 содержит целое число,в формате длинного слова и что оно заведомо
не больше 50 000. Требуется напечатать целое число с соответствующим сообщением.
Приведенные далее ассемблерные директивы определяют сообщения и резер-
вируют необходимую для преобразований область памяти:
LINEi .ASCII /Ответ равен/
LSNi .BLKB 7 j 6 байт для символьного кода и один для нулей
PKDi .BLKB 3
Пусть содержимое регистра R4 составляет 000001ЕЗ. Тогда выполнение
команды
CVTLP R4, ф 5, PKD преобразовать в упакованное
даст такой результат:
PKD
00 48 ЗС
а команды
CVTPS Ф 5, PKD, Ф 5, LSN преобразовать в символьный код
следующий результат:
LSN
2В 30 30 34 38 33 = ’+’ ’0’ ’0’ ’4’ ’8’ ’3’
102
Сообщение и число печатаются по команде
PRINTCHRS LINE -.Напечатать результат
Будет напечатана следующая строка:
Ответ равен 4-00483
Обычно нежелательно печатать знак «+» и нули старших разрядов. Их можно
удалить с помощью команд, с которыми мы познакомимся ниже.
6.6. Заключение
В системе VAX имеются команды знаковой целочисленной ариф-
метики для выполнения сложения, вычитания, умножения и деления
байтов, слов и длинных слов. Предусмотрены двух- и трехоперанд-
ные варианты всех команд. При целочисленном делении частное
усекается в направлении к нулю.
Команды, оперирующие байтами и словами, при указании ре-
гистра используют только правый байт или слово регистра соответст-
венно.
Предусмотрены специальные команды инкремента, декремента,
сброса и изменения знака.
Команды целочисленной арифметики и большинство остальных
команд, рассмотренных в настоящей главе, устанавливают биты ко-
да условия в PSW, показывающие знак результата и переполне-
ние. В табл. 6.1. приведены диапазоны целых чисел, которые мож-
но хранить в различных единицах памяти.
Таблица 6.1
Диапазоны целых чисел
Тип данных Число битов Диапазон (знаковый) Диапазон (беззнаковый)
п _ 2л—| ... 2П—1 —1 0...2" — 1
Байт 8 —128...127 0...255
Слово 16 —32768...32767 0...65535
Длинное слово 32 —2147483648. ..2147483647 0...4294967295
Тетраслово 64 £63 £63 | 0...264—1
Команда SOBGTR управления циклом применяется для выпол-
нения команд в цикле заданное число раз.
Команды MOV используются для копирования данных, адре-
суемых первым операндом, в ячейку, адресуемую вторым операндом.
юз
о
Команды манипуляции целыми числами
Таблица 6.2
Команда Тип данных Операция
Арифметические команды ADDx2 onl, оп2 ADDx3 onl, оп2, сумма SUBx2 onl, оп2 SUBx3 onl, on2, разность MULx2 onl, on2 MULx3 onl, on2, произведение DIVx2 onl, on2 DIVx3 onl, on2, частное INCx операнд DECx операнд MNEGx источник, получатель CLRx получатель Команда цикла SOBGTR индекс, назначение Команды копирования _ MOVx источник, получатель MOVAx источник, получатель M0VC31 длина, источник, получатель Команды преобразования данных СУТху источник, получатель CVTxf/2 nl, ист, п2, плч CVTPL2 п, упк,-^длинное CVTLP2 длинное, п, упк 1 Используются R0— R5. г Используются RO — R3. х=В, W или L » » » » х=В, W, L, Q или 0 индекс — длинное слово х = В, W, L, Q или О » х и у=В, W или L x=S и у=Р или х=Р и y—S nl и п2 — слова (0—31) п — слово » onl 4- оп2-*~оп2 onl 4- оп2-*~сумма оп2—оп1-+юп2 опЗ—onlразность оп1*оп2-+оп2 оп1*оп2-+произведение оп2/оп1-+оп2 оп2/onl-^частное операнд +1-^операнд операнд— 1 -^операнд — источник-^получатель 0-^получатель индекс— индекс; если индекОО, пе- рейти к назначению исто чник-^п ол у чате л ь адрес источника-^получатель источник-* полу чате ль (длина=число байт) источник (типа х), преобразованный в тип //-»► получатель ист (с nl цифрами), преобразованный из типа х в тип у (с п2 цифрами) плч упк (с п цифрами), преобразованный из упакованного в дополнительный-^длмк- ное (слово) длинное (слово), преобразованное из дополнительного кода в упакованный (с п цифрами)-+упк
По команде MOVA адрес первого операнда загружается вячейку,
определяемую вторым операндом, а по команде MOVC3
символьные цепочки копируются из одной области памяти в другую.
Команды CVT (преобразовать) предназначены для преобразо-
вания типов данных. Преобразования между типами целочислен-
ных данных элементарны: преобразование в более длинное число
осуществляется расширением знака, т. е. копированием знака в
левую часть результата, а преобразование в более короткое число —
отбрасыванием битов слева (в последнем случае может возник-
нуть переполнение).
В табл. 6.2. приведены все рассмотренные здесь команды.
Регистр или переменная, используемая для адресации данных,
называется указателем.
Завершая главу, мы перечислим некоторые рекомендуемые пра-
вила программирования:
1. Необходимо тщательно документировать программы. В прог-
рамму следует включить формулировку проблемы, описание исполь-
зуемых данных, пояснение метода решения проблемы, список и наз-
начение используемых регистров, краткие комментарии действий
команд и пустые строки комментариев для разделения секций
программы.
2. Промежуточные результаты целесообразно хранить в регист-
рах, а не в ячейках памяти, так как обращения к памяти требуют
больше времени.
3. Нельзя разрушать и модифицировать входные данные, если
в этом нет необходимости. Данные могут потребоваться в других
секциях программы.
6.7 Упражнения
1. Напишите команду для деления числа, содержащегося в слове EGGS, на 12
и запоминания частного в слове DOZENS.
2. Напишите команду для вычитания байта в USED из байта в ONHAND
с сохранением результата в ONHAND.
3. Напишите команды для вычисления выражения (5х1 2 3 4—27х2 + 32) и запоминания
результата в слове VALUE. Считайте, что х находится в слове DATUM и что
переполнение исключено.
4. Пользуясь представлением отрицательного числа п в дополнительном коде
как 2s—]п|, где s — число битов в представлении (гл. 3), докажите, что
метод вычитания, рассмотренный в разд. 6.2, независимо от знаков операндов дает
правильный результат (переполнение исключено).
5. На рис. 6.1. показано умножение двух положительных чисел. Пользуясь
этим же методом, умножьте два отрицательных числа в форме байта. Результат
получился правильным?
105
' - 6. Какова взаимосвязь между знаками делимого и остатка (если остаток не равен
нулю) при делении целых чисел? Всегда ли они одинаковы? Всегда ли противополож-
ны? Иногда одинаковы, а иногда противоположны? Обоснуйте свой ответ
(можно на примерах).
7. Напишите четыре различных команды, по которым все биты регистра R9
сбрасываются в нуль.
8. В. программе, написанной на языке высокого уровня, имеется следующий
оператор:
A=(7*/-|-J/LL)** 2
Все переменные представляются целыми числами в формате длинного слова, а две
звездочки означают возведение в степень. Напишите эквивалентную ассемблер-
ную программу.
9. Напишите команды для определения прибыли, которую вы получите,
организовав благотворительный обед. Воспользуйтесь следующими данными: реклама,
гонорар ораторам и другие фиксированные расходы составят 527 дол., стоимость
одного обеда — 19 дол. (с учетом налогов и чаевых), стоимость одного билета —
30 дол., или 225 дол. на группу из восьми человек. Всего продано 78 билетов на одно
лицо и два групповых. Выберите приемлемый размер данных и включите в программу
директивы инициализации переменных.
10. Ниже показано содержимое регистров и памяти до выполнения приведенных
далее команд. Определите новое содержимое всех регистров и ячеек памяти,
модифицированное каждой командой, и укажите, какие команды вызовут
переполнение.
Регистры Содержимое памяти Адрес ячейки
R6 000184А0 R7 FFEAFF2C R8 00002479 R9 00148DE8 a) ADDW2 00275041 FFFFFE75 00000В42 00175CD3 (R9) + ,(R6) б) R7, R6, (R9) г) 184 АО 184А4 148DE8 148DEC D1VB2 #4, R7
в) D1VB3 CVTWL R7, R8
д) CVTWB R7, R7 е) MNEGW R6, R6
ж) DECB (R6) з) CLRW (R6)
। 11. В примере 6.5. для вычисления стоимости заказа с учетом 20% скидки
применялись следующие команды:
DIVL3 #5, TOTAL, R7 ; Вычислить скидку
SUBL3 R7, TOTAL, AMNT ; Вычесть скидку
Предположим теперь, что вместо них программист написал другие команды:
MULL3 #4, TOTAL, R7 ; 4* TOTAL
DIVL3 #5, R7, AMNT ; 80% общего
Будет ли результат в AMNT одинаковым в обоих случаях? Поясните все различия
между приведенными командами в отношении их понятности и быстродействия.
12. Напишите команды для вычисления среднего возраста по пяти данным,
хранимым в массиве AGES слов, и поместите результат в слово AVERAGE.
Не пользуйтесь циклом.
106
13. Допустим, необходимо разделить положительное длинное слово в ALPHA на
положительное длинное слово из ВЕТА и поместить результат в длинное слово
GAMMA. Требуется получить округленный, а не усеченный результат. Напишите
команды для решения поставленной задачи (достаточно трех команд). Правилен
ли ваш способ, если делитель и (или) делимое могут быть отрицательными?
Если да, то поясните, почему. Если нет, приведите пример, показывающий, когда
этот способ дает неправильный результат.
14. Представьте себе, что VAX не имеет специальных команд INCx, DECx,
и MNEGx. Для каждой из следующих ниже команд напишите по одной
соответствующей команде с теми же функциями: a) 1NCW R8, б) DECL (R6) + ,
в) MNEGL R5, R10.
15. Сложение тетраслов реализуется с помощью команд ADDL2 и ADWC
(сложить с переносом). Как и по ADDL2, по команде ADWC первый операнд
суммируется со вторым, но, кроме того, прибавляется значение бита переноса С.
Команды ADD устанавливают бит С, если возникает перенос из старшего бита.
Напишите команды прибавления тетраслова из QUAD к тетраслову в регистрах
R8 и R9 (младшая половина операнда находится в R8).
16. Рассмотрите крманды SBWC, EMUL и ED1V, приведенные в руководстве
VAX-11 Architecture Handbook, и напишите команды вычитания, умножения и деле-
ния тетраслов.
17. Каково будет содержимое регистров R6 и R9 по окончании выполнения
программы из примера 6.8?
18. Напишите команды для сложения всех элементов массива LIST длинных
слов и запоминания суммы в длинном слове SUM. Считайте, что длинное
слово KNT содержит число элементов массива.
19. Напишите команды для вычитания единицы из каждого четвертого элемен-
та массива COUNTS слов, начиная с четвертого элемента. Число элементов
массива указано в слове NUM. Можно допустить, что массив содержит минимум
четыре элемента, но число элементов массива может быть и не кратным четырем.
20. Предположим, что необходимо скопировать весь массив А1 слов в массив
А2 слов и что в длинном слово NUM указано число элементов. Такая задача
реализуется с помощью простого цикла, аналогичного рассмотренным в примерах
разд. 6.3. Но ее же можно решить, применив всего две команды — что это за
команды?
21. Задан массив DATA слов, число элементов которого хранится в длинном слове
N. Пусть элементы массива обозначены как а\, аг, .... ап, где n = (N).
Для каждого i между 1 и п определим Лю частичную сумму: а\ + аг+ ... + az, т. е.
сумму первых i элементов. Напишите сегмент программы для замены элементов
частичными суммами. Следовательно, по завершении выполнения программы
в DATA [Z] должна быть i-я частичная сумма. (Эту задачу можно решить с помощью
короткого цикла — из двух команд, пользуясь автоинкрементным режимом адре-
сации. См. также пример 6.8.)
22. По командам MOVZjq/ (переслать с нулевым расширением), где
jq/=BW, BL и WL, первый операнд расширяется путем дополнения слева нулей, а
результат помещается во второй операнд. Чем эти команды отличаются от
команд CVT? Можно ли в примере 6.15 применить команду MOVZ вместо одной из
команд CVT?
23. Элементы массива DELTA байтов вычитаются из соответствующих элемен-
тов массива VALUES длинных слов; новые значения должны замещать старые в мас-
сиве VALUES. Число элементов каждого массива хранится в длинном слове
POINTS. Напишите команды для решения поставленной задачи.
24. Предположим, что значение регистра R3 равно 00029А7С, значение регистра
R4 — FFED5836, и два длинных слова в памяти по адресу 29А7С содержат
00000028 и 003СЕ737. Покажите, каково будет содержимое регистров R3 и R4
после выполнения каждой из следующих команд: a) MOVL R3, R4, б) MOVL (R3),
R4, в) MOVB R3, R4, г) MOVB (R3) 4- R4.
25. Считая исходное содержимое регистров и памяти идентичным указанному
в упр. 24, найдите новое содержимое R3 и R4 после выполнения каждой из следующих
команд: a) MOVAL (R4) +, R4, б) MOVAB (R3)+, R4.
107
26. Используя формат записей, описанный в упражнении 9 гл. 5, напишите
программу для считывания всех записей файла и запоминания их в символьном
массиве MAILING___LIST. Затем программа должна форматировать и выводить
элементы на печать, как и в у пр. 9, но оставлять после каждого из них пустую
строку. Программа должна также резервировать память на 20 элементов и проверять
считывание и запоминание не более 20 элементов.
27. Допустим, в памяти, начиная с адреса TEXT, находится последовательность
строк переменной длины, причем длина каждой строки хранится в байте перед ее
первым символом, например:
TEXT
05 'Н' 'Е' 'L' 'L' 'О' OB 'S' 'Е' ZC' 'О' 'N' ZDZ ' ' 'L' 'I' 'N' 'Е'
V.--—у---------- >--------------у-- ---------------
Строка 1 Строка 2
Здесь 05 — число символов в строке 1, а 0В (т. е. одиннадцать)—число
символов в строке 2.
Предположим, что регистр R8 содержит адрес байта длины одной из строк.
Напишите команды для печати этой строки и формирования в R8 адреса байта
длины следующей строки.
28. Пусть MATRIX — двумерный массив длинных слов, размеры которого
указаны в длинных словах ROWS (строки) и COLMS (столбцы). Нумерация
строк и столбцов начинается с единицы. Считая, что длинные слова 1 и J содержат
номера строки и столбца, напишите формулу для адреса элемента, который в язы-
ках высокого уровня обозначается кай MATRIX [I, J]. Решите эту же задачу для
массива, в котором номера строк и столбцов начинаются с 0.
29. Представьте число — 3105 в символьном коде и упакованном десятичном
формате.
30. Ниже приведены данные и команды. Возможен ли при выполнении каждой
из этих команд особый случай операнда, и, если нет, покажите, что именно она
загрузит в байты, начинающиеся с PKD.
LINE
20 20 2D 31 37 30 20 20 35 33 36 20 20
a) CVTSP #3,LINE+2, #3,PKD
б) CVTSP #2,LINE+7, #2,PKD .
в) CVTSP #3,LINE+8, #3,PKD
31. Напишите команды для преобразования из дополнительного кода слова в
ALPHA в символьный и печати этого слова с предшествующим сообщением
«ALPHA = >.
32. Напишите команды для преобразования и печати всех целых чисел из массива
NUMBERS длинных слов, считая, что в длинном слове NUM указано число элементов
и что каждый элемент имеет максимум шесть разрядов. Числа необходимо печатать
в столбце с соответствующим заголовком.
33. Напишите законченную программу, которая считывала бы с терминала
I, J и LL, вычисляла выражение из упр. 8 и печатала К. В документацию программы
должно быть включено описание формата входных данных.
34. Напишите программу для ввода, преобразования в дополнительный код
и запоминания данных в двумерном массиве длинных слов. Можно считать, что все
входные числа имеют пять разрядов (включая нули старших разрядов) и размещены
во входной строке в стандартном формате. Начальная входная строка содержит
размеры массива, причем первым указывается число строк. Число столбцов должно
быть не больше 12, чтобы каждая строка матрицы помещалась в одной входной
строке. После строки с размерами массива вводятся его строки.
35. Напишите команды для печати двумерного массива длинных слов. Элементы
массива имеют максимум по пять значащих цифр, а длинные слова ROWS и COLMS
содержат число строк и столбцов соответственно. Считайте, что максимальное
число столбцов равно 10. Если вы уже выполнили упр. 34, объедините его результат
с программой этого упражнения и выполните ее для ввода, запоминания и последую-
щей печати массива.
Глава 7
Переходы и циклы
7.1. Коды условий и переходы
Коды условий и команды переходов*. Командой перехода называ-
ется команда, по которой в программный счетчик PC загружается
новый адрес, и следующей будет выполняться команда по этому
новому адресу, а не команда, физически следующая в памяти.
Большинство команд переходов являются командами условных пере-
ходов, т. е. они могут изменять значение PC или не изменять в зави-
симости от особенностей обработанных данных. Для регистрации
особенностей операндов команд в системе VAX, как и в большинстве
других ЭВМ, применяются однобитовые флаги, называемые
кодами (флагами) условий. Команды условных переходов прове-
ряют эти флаги и определяют, изменять значение PC или нет.
В системе VAX кодами условий являются первые (правые)
четыре бита PSW:N, Z, V и С. Они размещаются в PSW следующим
образом:
PSW N Z V С
Ниже перечислены наиболее часто встречающиеся коды условий:
N — отрицательный. N= 1, если результат операции отрицатель-
ный; после сравнения N=l, если первый операнд меньше второго;
Z—нуль. Z=l, если результат операции равен нулю; после
сравнения Z=l, если сравниваемые операнды равны;
V — переполнение, V=l, если результат операции не умещает-
ся в отведенном ему месте;
С — перенос. С=1, если операция вызвала перенос или заем
из левого бита; после целочисленного сравнения С= 1, если первый
операнд меньше второго (как беззнаковые числа).
Таким образом, условный переход складывается из двух этапов.
Сначала устанавливаются коды условий либо явно по команде
проверки или сравнения, либо неявно как побочный результат
1 Более точный термин: «Команды передачи управления».— Примеч. пер.
109
выполнения другой операции. Большинство команд влияет на коды
условий. Все команды целочисленной арифметики, пересылок и пре-
образований воздействуют на биты N, Z и V и показывают, что
результат операции отрицательный, нулевой или вызывает перепол-
нение. (При переполнении N и Z соответствуют условию истинного
результата, а не усеченной части, запоминаемой в операнде-полу-
чателе). Команды целочисленной арифметики влияют на бит С,
что свидетельствует о возникновении (отсутствии) переноса или
заема из левого бита. Арифметические команды для других типов
данных (плавающая точка и упакованные десятичные числа)
воздействуют на биты N и Z аналогичным образом; интерпретация
V и С зависит от конкретной команды. Далее при обсуждении
новых команд мы объясним их воздействие на коды условий. Более
подробная информация имеется в руководствах VAX-11 Program-
ming Card и в VAX-11 Architecture Handbook.
Второй этап условного перехода заключается в проверке кодов
условий — должен ли осуществляться переход; при положительном
результате адрес перехода загружается в PC. Обычно второй этап
реализуется с помощью команды условного перехода, но есть коман-
ды, по которым оба этапа совмещаются. Например, по команде
SOBGTR производится вычитание из индекса цикла, устанавливают-
ся коды условий, затем эти коды проверяются и выполняется переход,
если соответствующее условие удовлетворяется.
В системе VAX предусмотрен обширный набор команд условных
переходов, которые удобно разделить на три группы:
• используемые после операций над знаковыми данными (целые
числа в дополнительном коде, числа с плавающей точкой и упако-
ванные десятичные числа);
• используемые после операций над беззнаковыми данными
(например, беззнаковые двоичные целые числа, адреса, символьные
цепочки);
• проверяющие перенос и переполнение.
Программист интерпретирует команду перехода в зависимости
от контекста, в котором установлены коды условий. Например,
после выполнения проверки или арифметической операции они
отражают особенности одного данного, а после сравнения характе-
ризуют отношение между двумя сравниваемыми данными. Централь-
ный процессор интерпретирует команды условных переходов как
указания о конкретных проверках кодов условий. Так, команда
BEQL вызывает переход, если (и только если) бйт Z содержит 1,
а команда BGTR — если Z = 0 и N = 0. Сами команды условных
переходов не изменяют текущих значений кодов условий. Команды
переходов и их интерпретации приведены в табл. 7.1. В нее включены
также две команды безусловного перехода BRB и BRW, по которым
переход осуществляется независимо от значений кодов условий.
Формат любой команды перехода:
Ьхуг назначение
110
Команды переходов
Таблица 7.1
Команда Интерпретация программистом Перейти, если
После проверки перейти, если После сравнения перейти, если
Для знаковых данных BEQL BNEQ BGTR BLEQ BGEQ BLSS операнд=0 операнд#=0 операнд>0 операндов операнд>0 операнд <0 операнды равны операнды не равны оп!>оп2 оп1^.оп2 опГ^оп2 оп1<.оп2 Z=1 z=o N = 0и Z=0 N= 1 или Z = 1 N = 0 N=1
Для беззнаковых данных BxyzU (xyz— любая из шести комбинаций приведенных вы- ше) Аналогично предыдущим командам Как и выше, но с заменой N на С
Для V и С BVS BVC BCS вес перейти, если есть переполнение перейти, если нет переполнения перейти, если есть перенос перейти, если нет переноса — о — о II II II II »ии
Безусловные пере-
ходы
BRB | перейти в любом
BRW / случае
Проверки бита О
BLBSon, назн перейти, если бит 0 установлен не влияют
BLBC on, назн перейти, если бит 0 сброшен
Здесь хуг — одно из нескольких условий перехода. Назначение
определяется адресным выражением. Для команд переходов, при-
веденных в табл. 7.1, существует ограничение на расстояние назна-
чения перехода1. Во всех командах, кроме BRW, машинный код
позволяет закодировать это расстояние одним байтом, поэтому
он может составлять примерно до 127 байт от команды перехода.
В команде BRW расстояние назначения кодируется словом, поэтому
ее можно использовать для перехода к командам с большим расстоя-
нием назначения.
'Назначение — это команда (точнее, адрес ее первого байта), которой пере-
дается управление. Расстояние назначения — число байтов между командой перехо-
да (точнее, первого байта следующей за ней команды) и командой, которой пере-
дается управление.—Примеч. пер.
111
Отметим, что имеется несколько пар команд условных переходов,
в которых обе команды осуществляют переход при одинаковых
значениях кодов условий (например, BEQL и BEQLU). По существу,
это одна и та же машинная команда, но с разными мнемониками,
введенными для облегчения работы программиста.
Пример 7.1. .Условный переход.
Проблема. Некоторый банковский лицевой счет находится в длинном слове,
адрес которого хранится в регистре R8, содержимое регистра R6 составляет сумма,
указанная в выписанном на этот счет чеке. Плата за услуги составляет 20 центов.
Все денежные суммы записаны в виде целых чисел, отображающие центы. Необ-
ходимо вычислить и запомнить новое значение счета, если чек можно погасить;
в противном случае перейти к OVRDRN.
Обсуждение возможного решения. По командам SUB устанавливаются коды
условия и показывается знак разности. Следовательно, команду условного перехода
можно использовать сразу же после вычитания из общей суммы на счете суммы,
указанной в чеке, и проверки платы за чек. Вычитания следует производить
в регистрах, чтобы не изменять старый счет, если чек неплатежеспособен.
Решение:
I Использование регистров!
R6 сумма чека
R8 адрес счета
R1O рабочий
j Вычесть сумму чека
8UBL3 R6,(R8),R10
SUBL2 #20,R10
I Учесть плату за чек
BLSS OVRDRN
; Перейти, если счет превышен
MOVL RIO,(R8)
I Запомнить новый счет
Покажем установку кодов условий на конкретных данных. Пусть на счете
записано 150.00 дол., а чек выписан на сумму 149.95 дол. Результат первого
вычитания положителен, но результат, полученный после вычитания платы за чек,
отрицателен. Ниже приведены значения кодов условий после выполнения каждой
команды.
Команда
SUBL3
SUBL2
BLSS
N Z VC
R6,(R8),R10 0 0 0 0
#20,R10 10 0 1
OVRDRN не изменяются
Поскольку N=l, происходит переход к OVRDRN. По команде SUBL2 устанав-
ливается бит С, так как вычитание 20 из 5 требует заема в левом разряде:
1 00000005 5
00000014 20
FFFFFFF1 - 15^
Команды проверки и сравнения. Иногда требуется выполнить
условный переход в зависимости от значений данных, которые не
использовались в другой команде, повлиявшей на коды условий.
Коды условий можно установить явно с помощью команд TST
112
(проверить) и CMP (сравнить) ср следующими форматами для
целочисленных данных:
TSTx операнд х=В, W или L
СМРх оп\,оп2 х=В, W или L
Основное назначение этих команд — установка кодов условий,
операнды же их не изменяются. (Но, как и все остальные команды,
они могут изменить содержимое регистров, используемых для адре-
сации операндов, например в автоинкрементном режиме.)
По команде TST проверяется целое число в дополнительном
коде. Биты N и Z показывают, является ли операнд отрицательным
или нулевым; биты С и V сбрасываются (устанавливаются в нуль).
В соответствии с командами СМР два операнда сравниваются
как целые числа — знаковые в дополнительном коде или беззнако-
вые. Бит Z показывает, равны операнды или не равны, бит N —
меньше ли второго первый операнд (знаковые целые числа), а бит
С — меньше ли первый операнд, чем второй (беззнаковые целые
числа). Бит V сбрасывается.
Пример 7.2. Воздействие команды СМР на коды условия. При (ALPHA) = 6А и
(ВЕТА) =94 по команде
СМРВ ALPHA, ВЕТА
будут установлены коды условий:
N = 0, так как в дополнительном коде число (ALPHA) положительно, а число
(ВЕТА) отрицательно, (ALPHA) не меньше (ВЕТА);
Z =0, поскольку операнды не равны;
V = 0 всегда после выполнения команды СМР;
С= Г, потому что 6А<94 (беззнаковые двоичные числа).
Особенности программирования. При программировании необхо-
димо тщательно контролировать условные переходы. Рекомендуется
строить программу так, чтобы использовать минимум переходов и
(за исключением циклов) выполнять только переходы «вперед».
Весьма полезно выписать условные операторы языка высокого
уровня, а затем сформулировать простые правила по переводу их
на язык Ассемблера. Часто переход применяется для дифференциа-
ции двух случаев — в одном их них необходимо выполнить некоторые
дополнительные действия, а в другом ничего этого не требуется.
Подобная ситуация для оператора if представлена на рис. 7.1. Как
показано на рис. 7.1,в, программист должен выбрать некую
команду условного перехода, чтобы осуществить переход по условию,
для которого дополнительные команды не выполняются. Изложенные
выше правила при этом соблюдаются. Компиляторы транслируют
оператор if именно таким образом.
а) Оператор if
if < условие > then < оператор >
113
(Команды проверки < условия »
Bxyz NEXT ; xyz есть отрицание < условия >
(Команды, реализующие < оператор »
NEXT: Следующий оператор
Рис. 7.1. Трансляция оператора if на язык Ассемблера
Пример. 7.3. Оператор if-then.
Проблема. В массиве фамилий NAMES каждая фамилия имеет длину в 22
символа. Массив слов SCORES (оценки) содержит целые числа, все его элементы
ассоциируются с соответствующими позициями массива NAMES. В длинном слове
COUNT хранится число элементов обоих массивов. Требуется напечатать фамилии,
ассоциируемые с оценками в пределах 75—85 или другими словами:
if 75<оценка<85 then напечатать фамилию
Решение!
I
| ФОРМУЛИРОВКА ЗАДАЧИ
I Этот программный сегмент просматривает массив SCORES и печатает
I Фамилии ио массива NAMES ио тех позиций, которые соответствуют
I оценкам, находящимся между LOW и HIGH включительно.
I
| ДАННЫЕ
I
I SCORES массив слов
1 NAMES символьный массив, элементы которого содержат по LEN
I символов
I COUNT число элементов в массивах (длинное слово)
I LEN длина фамилии
114
| КОНСТАНТЫ
I
LOW - 75
HIGH - 85
LEN - 22
1 1 Использование регистров! R6 указатель SCORES
$ R7 указатель NAMES
1 RS счетчик цикла
1 MOVL COUNT,RS 5 Инициализировать счетчик цикла
BEQL DONE 1 Перейти, если массивы пусты
MOVAW * SCORES,R6 1 'Имициалиаировать указатель SCORES
MOVAB NAMES,R7 1 Имициалиаировать указатель NAMES
5
COMPRi CMPW (R6),#LOW 1 Сравнить оценку с LOW
BL88 NEXT 1 Если меньше, но печатать
CMPW (R6) 1 Сравнить оценку с HIGH
BGTR NEXT 1 Если больше, не печатать
PRINTCHRS (R7),#LEN 1 Напечатать фамилию
NEXT: ADDL2 #2,R6 1 Инкремент указателя SCORES
ADDL2 •LEN,R7 I Инкремент указателя NAMES
80BQTR RS,COMPR I Управление циклом
DONEi <следующая команда>
Примечание. Команды условных переходов выбраны так, чтобы выполнить
переход при отрицании условия в операторе if.
Документирование программы из примера 7.3 имеет одну особен-
ность: здесь приведены табличный список используемых данных, их
типы и назначение. Работать с такой таблицей чрезвычайно просто;
она весьма полезна даже в программах средних размеров как для
самого программиста (меньше вероятность ошибок), так и для тех,
кому придется работать с программой.
Почему бы для указателя массива SCORES не воспользоваться
автоинкрементным режимом? Если его ввести в первую команду
CMPW, регистр R6 не укажет на туже самую оценку при выполнении
второй команды CMPW, что может привести к неправильным резуль-
татам. Если же автоинкрементный режим ввести во вторую команду
CMPW, то после перехода к метке COMPR будет неизвестно, указы-
вает ли R6 на следующую оценку или адресует предыдущую,
так как вторая команда CMPW выполняется не в каждом проходе
по циклу. Поэтому команда с меткой NEXT всякий раз производит
явный .инкремент регистра R6.
Оператор if-then-else отражает ситуацию, когда в зависимости
115
от некоторого условия выполняется одна из двух альтернативных
последовательностей операций. Блок-схема и шаблон ассемблерных
операторов для такой ситуации представлены на рис. 7.2.
а) Оператор if - then - else
if < условие > then < оператор—1 >
else < оператор_2 >
б) Блок-схема оператора if — then — else
в) Конструкция ассемблерных операторов
(Команды проверки < условия »
Bxyz TRUE ; Перейти, если условие истинно
(Команды, реализующие < оператор—2 »
BRB NEXT
TRUE:
(Команды, реализующие < оператор—1 »
NEXT: Следующий оператор
Рис. 7.2. Трансляция оператора if-then-else
Пример. 7.4. Оператор if-then-else. Программа формирует и выводит на пе-
чать одно из двух сообщений в зависимости от значения проверяемого данного. Задачу
поясняет оператор:
if альфа = 0 then напечатать «ALPHA IS ZERO»
else напечатать «ALPHA IS NOT ZERO»
Решая задачу на языке Ассемблера, можно записать:
Инициализировать сообщение на «ALPHA IS»
if (ALPHA) = 0 then переслать в сообщение «ZERO»
else переслать в сообщение «NOT ZERO»
Напечатать сообщение
Ниже приведен сегмент программы. Отметим, что для'запоминания цепочки
«ZERO» использована директива .ASCIZ, поэтому в конце ее будет помещен нуле-
вой байт. Дополнительный байт учитывается в командах MOVC3.
116
MSGi .ASCII /ALPHA IS /
ANSI .BLKB 9
NOT: .ASCII /NOT /
ZERO: .ASCIZ /ZERO/ ; Конец сообщения отмечен нулями
TSTL ALPHA
BEQL IS.O
M0VC3 «9,NOT,ANS
BRB PRINT
IS_Oi M0VC3 #5,ZERO,ANS
PRINT: PRINTCHRS MSB
I Проверить ALPHA
I Перейти, если ALPHA О
; Переслать "NOT ZERO" в сообщение
I Перейти к печати
I Переслать "ZERO" о сообщение
I Напечатать сообщение
Сравнение символьных цепочек. Для сравнения символьных цепо-
чек предусмотрены две команды. Одна из них, более простая,
СМРСЗ длина', цепочка!, цепочкаЗ
позволяет сравнивать длинные цепочки байтов. Байты не обяза-
тельно должны содержать символьные коды, хотя обычно команда
СМРСЗ применяется для сравнения именно символьных цепочек.
Как и команды TST и СМР, она не модифицирует операнды; ее
главное назначение — установить коды условий.
Первый операнд задает длину (т. е. число байтов) в каждой
цепочке. Он должен быть целочисленным словом и может определять-
ся литеральным или любым другим режимом адресации. Разумеется,
цепочки должны находиться в памяти.
По команде СМРСЗ соответствующие байты в двух цепочках,
сравниваются до тех пор, пока не будут обнаружены неравные
байты или не достигнут конец цепочек (определяемый операндом
длины). Она устанавливает коды условий аналогично командам
СМР для целых чисел; другими словами, она осуществляет сравне-
ние знаковых и беззнаковых чисел (байтов).
Символьные коды ASCH букв алфавита следуют в численном
порядке, т. е. символьный код «А» меньше код# «В» и т. д. В
связи с этим значения кодов условий после команды СМРСЗ можно
использовать для сортировки (упорядочения по алфавиту) символь-
ных данных. Читателю рекомендуется изучить символьный код
ASCH по приложению С, чтобы знать, как группы символов разме-
щены в последовательности кодов. Например, прописные буквы
предшествуют строчным, а цифры предшествуют буквам. Код
пробела меньше кодов букв и цифр, поэтому, например, цепочки
«MILL » и «MILLER» можно упорядочить обычным образом.
Если сравниваемые цепочки содержат символьные коды, биты
117
N и С имеют одинаковые значения, так как все символьные коды
ASCII содержат нуль в левом бите и интерпретируются как
положительные целые числа. Поэтому в сочетании с командой
СМРСЗ при просмотре и сортировке символьных данных можно
применять команды условных переходов для знаковых и беззнаковых
целых чисел. Необходимо отметить, что при выполнении команды
СМРСЗ используются регистры R0—R3, и что их исходное содержи-
мое разрушается.
Пример 7.5. Сравнение символьных цепочек. В регистре R7 хранится адрес
восьмисимвольного имени, а второе имя находится в памяти сразу вслед за первым.
Если имена не упорядочены по алфавиту, их необходимо поменять местами.
Другими словами, задача состоит в том, чтобы запрограммировать следующий
оператор:
if первое имя>второе имя then поменять имена местами.
В программе предполагается наличие восьмисимвольной рабочей области TEMP.
ADDL3 #8,R7,R8 j Адрес второго имени
СМРСЗ #8,(R7),(R8) ; Сравнить имена
BLEQ NEXT ; Перейти, если имена по порядку
M0VC3 #8,(R7),TEMP $ Передать первое имя о TEMP
MOVC3 #8,(R8),(R7) | Передать второе имя на место первого
M0VC3 #8,TEMP,(R8) ; Передать первое имя на место второго
NEXTi <следующая команда>
(В гл. 8 рассмотрен еще один режим адресации — режим смещения, который
позволяет адресовать второе имя без дополнительного регистра.)
Во многих применениях необходимо проверить, не содержит ли
байт конкретный символ. Например, программа ввода символов с
терминала должна контролировать, не является ли введенный
символ символом возврата каретки. Конечно, проверить символ
можно и с помощью команды СМРСЗ, задав длину 1, но предпочти-
тельнее воспользоваться командой СМРВ, поскольку она короче и
допускает литеральный операнд. Следующая команда проверяет байт
в CHAR — содержит ли он символ « + » (или, точнее, код этого
символа:)
СМРВ char,#aa/+/
Однобитовые флаги. Однобитовые флаги применяются для разных
целей, например фиксации состояния задач, выполняемых различны-
ми программами, и устройств ввода-вывода, а также идентифи-
кации свойств данных. В процедурах, особенно осуществляющих
ввод, удобно возвращать в вызывающую программу флаги, показы-
вающие, успешно или нет завершены задачи. Когда требуется
несколько флагов, правильный выбор позиций для них в регистре
или слове памяти позволяет упростить и сократить программу.
Во многих ЭВМ идеальной позицией для одного флага .оказывается
знаковый бит благодаря наличию простых команд проверки знака.
Если, например, флаг находится в бите 7 байта STATUS, его
можно проверить и выполнить условный переход с помощью следую-
щих команд:
118
TSTB STATUS
BLSS STATUS__1 ; Перейти, если бит 7=1
STATUS—0: ; Бит 7 = 0
В системе VAX предусмотрены две специальные команды, кото-
рые делают бит 0 лучшей позицией для флага. Это команды BLBS
(перейти, если младший бит установлен, т. е. содержит 1) и BLBC
(перейти, если младший бит сброшен, т. е. содержит 0). Обе команды
инициируют и указанную проверку, и условный переход. Они не
модифицируют и не проверяют кодов условий. Формат этих команд:
BLBx операнд, назначение
Здесь х— S или С. Хотя проверяется только бит 0, предполагается,
что первый операнд является длинным словом. (Это важно при выбо-
ре режима адресации, например автоинкрементного.) Назначение
перехода должно находится на расстоянии примерно 127 байт от
команды BLB.
Команда JMP. В командах безусловного перехода BRB и BRW
назначение должно быть' определено как адресное выражение.
Как правило, этих двух команд при программировании достаточно,
но иногда возникают ситуации, когда адрес назначения невозмож-
но определить в режиме перехода. Для таких ситуаций и предусмот-
рена команда JMP (перейти). Ее формат
JMP назначение
причем назначение можно определить почти в любом режиме адре-
сации (регистровый и литеральный режимы не допускаются). При
выполнении команды JMP процессор обычным образом вычисляет
адрес операнда и загружает его в PC, так что следующей будет
выполняться команда по этому адресу. Если, например, содержимое
регистра R9 составляет 00028Е47, то по команде
JMP (R9)
значение 00028Е47 будет загружено в PC.
Большинство практических применений команды JMP связано
с более сложными режимами адресации и способами программиро-
вания, которые здесь еще не рассматривались, поэтому мы не приво-
дим примеры ее использования.
7.2. Двоичный поиск (пример)
Проблема поиска. При разработке многих прикладных программ
приходится сталкиваться с поиском элемента в таблице. Элемент
таблицы может включать несколько взаимосвязанных данных. Если,
например, считать таблицей словарь, то каждый элемент словаря
содержит собственно слово, его транскрипцию и семантику,
а также происхождение и примеры употребления. Элемент в теле-
119
фонном справочнике состоит из фамилии, адреса и номера телефона.
Элемент в таблице имен (ассемблера или компилятора) включа-
ет имя, его значение и какую-то дополнительную информацию.
В любом случае одно из составляющих элемента идентифицирует
именно этот элементов приведенных примерах идентификаторами
служат слова, фамилия и имя. Идентификатор элемента таблицы
часто называют ключом. Обычно при поиске элемента ключ известен
и требуется получить остальную информацию, содержащуюся в
элементе, а иногда просто проверить, имеется ли заданный
ключ в таблице. Рассматриваемый здесь процесс нахождения эле-
. мента по его ключу называется поиском.
Наиболее очевидное решение проблемы поиска заключается в
последовательном сравнении заданного ключа с ключами элементов
до тех пор, пока не будет найден элемент или исчерпан список.
Такой метод известен как последовательный поиск. Программиро-
вание последовательного поиска не вызывает затруднений и предо-
ставляется читателю в качестве упражнений. Недостаток этого ме-
тода — громоздкость: в среднем приходится просматривать поло-
вину списку, а в худшем случае—.весь список; Если элементы
рассортированы по ключам (т. е. ключи следуют в численном
или алфавитном порядке), намного более эффективным оказывает-
ся другой метод — двоичный поиск.
Алгоритм двоичного поиска. Будем считать, что ключи в просмат-
риваемой таблице отсортированы в возрастающем порядке. При
описании алгоритма естественно использовать имена переменных в
том виде, в каком они фигурируют в языках высокого уровня,—
как имена данных, а не адресов. При рассмотрении алгоритма
двоичного поиска имена переменных выделяются курсивом.
Пусть key обозначает отыскиваемый ключ. Предположим,
что key сравнивается с ключом элемента где-то в середине таблицы
и оказывается, что он меньше этого ключа. Следовательно, если
key имеется в спйске, он должен находиться в первой его половине,
так как во второй половине списка все ключи больше, чем тот,
который находится в середине, и поэтому больше key (рис. 7.3).
Таким образом, выполнив всего лишь одно сравнение, мы свели
проблему поиска key по всей таблице к проблеме поиска его в поло-
вине таблицы. Этот прием повторяется до тех пор, пока не будет
найден ключ key либо пока не выяснится, что его в таблице нет:
key всегда сравнивается с ключом среднего элемента в некоторой
секции просматриваемой таблицы. Если key больше ключа среднего
элемента, поиск ведется во второй половине секции, а если меньше,—
в первой. Разумеется, как только ключ среднего элемента оказыва-
ется равным key, поиск прекращается. Эффективность двоичного
поиска очевидна, поскольку резко сужается круг просматриваемых
элементов.
Как показано на рис. 7.3, переменные first (первый) и last (послед-
ний) соответствуют началу и концу просматриваемой секции табли-
120
Ключи
Другая информация
На данном шаге
поиск ведется в этой <
секции таблицы:
key сравнивается с к
Если key < к, далее
искать в этой секции
Если key > к, далее
искать в этой секции
Рис. 7.3. Принцип двоичного поиска. Предполагается, что все клю-
чи разные, хотя поиск возможен и в том случае, если это не так
цы. Индекс проверяемого элемента — middle (средний) — вычисля-
ется путем усреднения first и last. Число элементов в таблице обозна-
чим переменной Num.
Предположим, что результатом поиска должен быть индекс
элемента таблицы, содержащего ключ key, или нуль, если такого
элемента в таблице нет. Ниже приведен алгоритм поиска на не-
формальном языке высокого уровня. Отметим, что в операторе
while предусмотрены две проверки: первая (index = 0) показывает,
что key еще не найден, а вторая (first^last), что подлежащая
проверке секция не пустая. Если first>last, элемента с ключом
key в таблице нет.
first: = 1; last: = num;
index: = 0;
while (index = 0) and (first < last) do
begin
middle : = (first 4- last)/2;
case
key = table(middle) : index : = middle;
key < table(middle) : last : = middle—1;
key > table(middle) : first : = middle+1
* endcase
end
На рис. 7.4 показаны просматриваемые секции таблицы
и сравнения, выполняемые в примере.
Адресация элементов таблицы. Большинство операторов алгорит-
ма поиска легко программируется на языке Ассемблера. Однако
возникает вопрос о том, как найти ключ элемента с индексом middle?
Попробуем вывести формулу для адреса ключа, .считая ключ пер-
вым полем элемента таблицы. Поскольку теперь придется вернуться
121
Отыскиваемый ключ равен 603
Когда сегмент имеет четное число элементов, "средним1
является последний элемент в первой половине
Ключи
Шаги
102
347
406
425
508
537
594
603
626
649
727
795
923
Первое сравнение
Третье сравнение
-*— Второе сравнение
first = 1, last = 13,
middle s 7
603 >594
firstя 8, last = 13,
middle я 10
603 <649
first = 8, last = 9,
middle = 8
найдено 603
а) Просматриваемые подсекции
б) Определение first,
last и middle
Рис. 7.4. Пример двоичного поиска. Отыскиваемый ключ равен 603.
Когда сегмент имеет четное число элементов, «средним» является
последний элемент в первой половине
к адресным вычислениям, будем следовать общему правилу, соглас-
но которому имена (т. е. адреса) переменных пишутся прописными
буквами, а имена остальных данных — строчными. Для этого потре-
буются такие данные:
TABLE
index
— адрес таблицы;
— индекс искомого элемента;
size (размер) — число байтов в элементе.
Адресом первого элемента, являющимся и адресом его ключа,
служит TABLE, адреса второго и Третьего элементов (и их ключей)
равны TABLE + size и TABLE + 2*size соответственно. В общем слу-
чае для любого индекса между .1 и Num имеем:
адрес (index)-ro элемента = TABLE+ (index—\)*size
Единственная величина, которая изменяется в цикле двоичного
поиска,—индекс. Полученное выражение можно переписать в виде
адрес (index)-ro элемента = TABLE —size+index*size
Здесь TABLE—size можно вычислить вне цикла, оставив в цикле
для вычисления адреса элемента всего две операции (умножение
и сложение).
Программирование двоичного поиска. По существу, разработка
программы уже закончена, и теперь остается лишь следовать алго-
ритму, а также написать ассемблерные команды, реализующие каж-
дый из операторов. Рекомендуется всегда руководствоваться этим
122
first
last «•-num
Рис. 7.5. Блок-схема двоичного поиска
правилом — начать с составления либо алгоритма на языке высоко-
го уровня, либо блок-схемы, а затем уже писать ассемблерную
программу. Формулировку проблемы и составление алгоритма ее ре-
шения можно не объединять с проработкой деталей языка Ассембле-
ра. Программа на языке высокого уровня или блок-схема служит
как бы канвой для записи ассемблерных операторов. На рис. 7.5
приведена блок-схема двоичного поиска как промежуточный вариант
между программой на языке высокого уровня и ассемблерным
вариантом. Первая проверка в оператор while не включена, так как
можно выйти из цикла, когда ключ key найден. Кроме вычислений,
показанных на блок-схеме, в программе должны быть предусмотрены
инициализация и вычисление адреса элемента; необходимо просле-
дить за тем, чтобы в командах фигурировали данные допустимых
типов. При выборе арифметических команд следует помнить, что
двухоперандные команды короче трехоперандных, но их невозможно
использовать в тех ситуациях, когда операнды нельзя разрушать.
Программный сегмент двоичного поиска представлен на рис. 7.6.
Каждая группа команд, соответствующая элементу блок-схемы,
начинается с комментария, описывающего это соответствие. Отметим,
что продуманные метки облегчают понимание программы. В ка-
123
ЗАДАЧА
Этот программный сегмент осуществляет двоичный поиск о таблице для
нахождений индекса элемента с заданным ключом. Если ключа в таблице
нет, индекс устанавливается равным нулю.
ОПИСАНИЕ ТАБЛИЦЫ
Ключи (целочисленные слова) рассортированы в еоорастающем порядке.
Раомер элемента является входным данным, а не константой.
ВХОДНЫЕ ДАННЫЕ
TABLE просматриваемая таблица (адрес)
NUM число элементов (слово)
KEY отыскиваемый ключ (слово)
SIZE число байт в элементе (слово)
ВЫХОДНЫЕ ДАННЫЕ
INDEX индекс элемента, содержащего отыскиваемый ключ)
О, если ключа в таблице нет (слово)
ПЕРЕМЕННЫЕ, ИСПОЛЬЗУЕМЫЕ В ПРОГРАММЕ
First, last, middle, в соответствии с блок-схемой
ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ! ИНИЦИАЛИЗАЦИЯ CVTWL SIZE,R5 MOVAB TABLE,R6 SUBL2 R5,R6 MOVW KEY,R7 RS раомер элемента (число байт) R6 TABLE - раомер (для адресации) R7 ключ R8 -first (длинное слово) R9 last (длинное слово) RIO middle (длинное слово) R11 адрес проверяемого элемента 1 Раомер, преобраооеанный о длинное слово j TABLE - рбомер 1 Ключ (о регистре для быстрого доступа)
124
$
I Элемент 1 блок-схемы
MOVL CVTWL #1,R8 NUM,R9 I first <- 1 I last <- (NUM)
1 1 Элемент 2 блок-схемы
5 SEARCH.LOOPi
CMPL R8,R9 j Сравнить first и last
B8TR NOTFOUND j Если first > last, клфча нет
I
) Элемент 3 блок-схемы
I
ADDL.3 R8,R9,R10 ; first ♦ last
DIVL2 #2,RIO j middle <- (f irst-Hast) /2
5 ‘
I Элемент 4 блок-схемы
I
MULL3 R1O,R5,R11 I middle * раомер
ADDL2 R6,R11 1 Адрес элемента
CMPW R7, (RID Сравнить ключи
BEQL FOUND 1 Если ключи равны, выйти из цикла
B6TR RESET-FIRST 1 Если ключ больше, искать среди
I больших ключей
?
I Элемент 5 блок-схемы
I
RESET.LASTi
SUBL3 #l,R10,R9 । last <- middle - 1
BRB SEARCH-LOOP
I
; Элемент 6 блок-схемы
5
RESET.FIRSTi
ADDL3 #l,R10,R8 j first <- middle + 1
BRB SEARCH.LOOP
I
, Элементы 7 и 8 блок-схемы
I
NOTFOUNDi
CLRW RIO | index О О
FOUNDi MOVW RIO,INDEX j Запомнить индекс
Рис. 7.6. Программа двоичного поиска 125
честве комментария введена даже «метка» RESET___LAST.
First, last, middle и size являются длинными словами, так как они
используются при вычислении адресов. В командах, реализующих
оператор case, команды условного перехода следуют друг за другом.
Поскольку команды условного перехода не изменяют коды условий,
обе они проверяют коды условий, которые сформированы при срав-
нении ключей.
7.3. Команды
управления циклами
В системе VAX предусмотрено довольно много команд управления
циклами. В общем виде они инициируют инкремент или декремент
индексов (счетчиков) цикла и переход к определенному в команде
назначению в зависимости от отношения между новым значением
индекса и предельным значением цикла.
Команды управления циклом системы VAX должны находиться
в конце тела цикла, т. е. после выполняемых повторно команд.
Назначением перехода служит первая команда тела цикла. Такая
структура показана на рис. Т.Ч.а. Отметим, однако, что из-за
отсутствия проверки в начале цикла команды тела цикла всегда вы-
полняются минимум один раз, даже если индекс находится за
предельным значением цикла. Показанная на рисунке структура
соответствует оператору repeat-until языка высокого уровня. Так
как есть вероятность получения неправильных результатов, иногда
приходится проводить дополнительную проверку перед началом
цикла или выбирать другую его структуру. Альтернативный ва-
риант, соответствующий оператору while языка высокого уровня,
представлен на рис. 7.7,6.
Индекс цикла допускается использовать в вычислениях, выпол-
няемых в теле цикла, но его нельзя изменять.
Как отмечалось в разд. 7.1, условный переход состоит из двух
этапов: установки кодов условий и их проверки на необходимость
перехода. Обычно эти этапы выполняются двумя отдельными коман-
дами. В командах же управления циклами оба этапа совмещены:
коды условий устанавливаются в результате инкремента или декре-
мента индекса цикла..
Во всех командах управления циклами подразумевается мак-
симальное расстояние назначения.
Команды управления циклами удобно разделить на три группы
в зависимости от их гибкости. Рассмотрим сначала простейшие
из них.
Команды SOBGTR и SOBGEQ. Мнемоники этих команд явля-
ются аббревиатурами предложений «Вычесть единицу и перейти,
если больше нуля» и «Вычесть единицу и перейти, если больше
или равно нулю». Они имеют следующий формат:
126
repeat
begin
тело цикла,
. изменить индекс
end
until индекс перейдет предел
while индекс не перешел предела do
begin
тело цикла;
изменить индекс
end
6)
Рис. 7.7. Структуры циклов: а) тело цикла выполняется мини-
мум один раз; б) тело цикла может не выполняться ни разу
SOBGjq/ индекс, назначение
Индекс должен быть длинным словом, а назначение должно
находиться на расстоянии примерно 127 байт от команды цикла.
Описание команд:
SOBGTR SOBGEQ
индекс*- индекс — 1 индекс-*- индекс — 1
перейти, если индекс>0 перейти, если индекс>0
Команды AOBLEQ и AOBLSS. Эти команды отличаются от
предыдущих в двух отношениях: они инициируют инкремент индек-
са на единицу вместо декремента, и предельное значение для цикла
задается в них как операнд. Мнемоники команд означают: «При-
127
бавить единицу и перейти, если индекс меньше или равен предель-
ному значению» и «Прибавить единицу и перейти, если индекс
меньше предельного значения». Команды имеют следующий
формат:
AOBLjq/ предел, индекс, назначение
Описание команд:
AOBLEQ
индекс-*- индекс-}-1
перейти, если индекс<предела
AOBLSS
индекс-*- индекс-}-1
перейти, если индексСпредела
Команда AOBLEQ очень удобна для реализации оператора
DO языка Фортран или оператора for языка Паскаль с инкрементом
1. Цикл DO имеет такую форму:
DO п индекс= начальное_значение, предел
(тело цикла)
п CONTINUE
Этот цикл реализуется командами:
I R7 используется как индекс цикла
I
MOVL начальноо.змачомио,R7
LOOPi
<толо цикла>
AOBLEQ предел,R7,LOOP
I Инициализация,индекса
I Управление циклом
Операторы в цикле DO стандартного Фортрана выполняются
минимум один раз, даже если начальное значение индекса цикла
превышает предел, поэтому ассемблерный сегмент соответствует цик-
лу DO без дополнительных проверок. В некоторых более новых
версиях Фортрана (например, Фортран 77) и в языках, имеющих
оператор for, тело цикла не выполняется ни разу, если начальное
значение превышает предел. Оператор for языка Паскаль имеет такой
формат:
{ог<индекс>: = <начальное_значение>1о<предел><1о
<тело цикла >
Чтобы ассемблерный сегмент точно соответствовал циклу for
(что должен обеспечить компилятор), перед началом цикла можно
ввести дополнительную проверку, в результате которой осуществля-
ется переход к оператору, находящемуся за циклом, если начальное
значение индекса больше' предельного для цикла. Обобщенная
реализация принимает вид
CMPL начальное.оначение,предел
BBTR NEXT
128
MOVL
начально».значениеуR7
LOOP I
<толо цикла>
AOBLEQ продол,R7,LOOP
NEXT I
Пример 7.6. Простой цикл for.
Проблема. В примере 6.9 показана секция программы, которая запоминает
номер индекса элемента в каждом элементе массива LIST длинных слов. Действие
ее описывается следующим оператором for:
for i:= 1 to число элементов do
list [l]: = i
Для управления циклом в примере 6.9 использовалась команда SOBGTR.
Здесь же мы применим команду AOBLEQ. Так как I начальное значение
индекса цикла равно 1, явное сравнение начального и предельного значений не
требуется.
Решение!
I
| ФОРМУЛИРОВКА ЗАДАЧИ
I Этот программный сегмент запоминает номер индекса о каждом элементе
I массива LIST. Если массив пустойу но запоминается ничего. Индекс
I начинается с 1.
I
| ДАННЫЕ
I
I LIST массив длинных слов
I NUM число элементов (длинное слово)
I
| ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ R6
I R7
I R10
I
| ИНИЦИАЛИЗАЦИЯ
I
MOVL NUMyR7 |
BEOL DONE |
MOVL #lyR6 |
MOVAL LISTyRIO |
индекс цикла
предел- цикла
указатель массива
Продол цикла в R7
Если (NUM) О, конец
Инициализировать индекс цикла
Инициализировать указатель массива
| ЦИКЛ
I
STORE! * MOVL R6y ‘(R10)+ |
AOBLEQ R7yR6ySTORE |
DONEi <следующая команда>
5 Зак. 821
Запомнить индексу инкремент указателя
Инкремент индексау проверка и переход
129
Команда АСВх. В операторе DO Фортрана и в операторе for
некоторых языков (PL/1, Алгол) программист может определить
инкремент индекса цикла, отличающийся от ± 1. В ряде версий Форт-
рана и в других языках инкремент может быть положительным или
отрицательным. Команды АСВ обеспечивают реализацию таких бо-
лее гибких операторов управления циклами. В них также допуска-
ются параметры цикла, не являющиеся длинными словами; предель-
ное значение, инкремент и индекс могут быть даже числами с пла-
вающей точкой.
Мнемоника команды АСВ означает: «Прибавить, сравнить и пе-
рейти», а формат выглядит следующим образом:
АСВх предел, инкремент, индекс, назначение
Здесь х=В, W или L (а также число с плавающей точкой—
F, D, G или Н). Предел, инкремент и индекс должны быть, типа х.
Расстояние назначения от команды АСВ может быть до 32767 байт.
Описание команды:
индекс-*— индекс+ инкремент
если инкрементно и индекс^предела, то перейти
если иикремент<0 и индекс>предела, то перейти
Пример 7.7. Реализация цикла DO с помощью команды АСВ. Цикл:
DO 10 1 = J, N, INC
<тело цикла>
10 CONTINUE
где J, N и INC — целые слова, можно реализовать следующим образом:
I R9 используется для индекса цикла
I
MOVW J',R9 1 Индекс* <- (J)
LOOPt <тяло цикла>
АСТИ N,INC,R9tLOOP 1 Управление циклом
Предположим, что (J)=5, (INC)=3 и (N)=28. Тогда цикл выполняется для
значений индекса 5, 8, И, 14, 17, 20, 23 и 26. На последнем проходе, когда индекс
равен 26, команда АСВ увеличит его значение до 29. Так как 29 не меньше предела
(N), переход не производится и выполняется команда, следующая после команды
ACBW
Предположим, что (J) = 5, (INC) = —3 и (N) = —13. Тогда цикла выполняется
при значениях индекса 5,2, —1, —4, —7, —10 и —13. Так как инкремент отрицателен,
при каждом выполнении цикла переход осуществляется в том случае, если новое
значение индекса больше или равно предельному.
130
7.4. Преобразование
входных данных
в символьном коде (пример)
Проблема. Здесь мы рассмотрим преобразование символьного
кода в дополнительный без привлечения мощных команд CVTSP и
CVTPL. Существует несколько причин для обсуждения этой проб-
лемы. Во-первых, интересно знать, каким образом по командам
VAX фактически осуществляются преобразования. Во-вторых,
во многих ЭВМ, особенно в микропроцессорах, нет таких мощных
команд, и программист, работающий на языке Ассемблера, должен
представлять себе, как преобразовать входные данные. И наконец,
применяемый при преобразовании метод Горнера вычисления поли-
номов используется достаточно часто.
Предположим, что некоторое десятичное целое чйсло, представле-
но в символьном коде. Значение числа задано выражением:
d„x 10"+£-,х 10”-'+...+^Х lo’+dix ю' +£,
гдесС — значение цифры ^ для Это выражение представля-
ет собой частный случаи-полинома, который в.общем виде записы-
вается следующим образом:
₽W=Cn*" + Cn-lX"_|+- + C2x2+Clxl + C0-
Здесь (bgjCn и Ci — коэффициенты. В представлении целого числа
коэффициентами служат значения цифр, а х является основанием
системы счисления; в рассматриваемом случае х= 10. Таким обра-
зом, проблема преобразования символьного кода в дополнительный
будет решена, если разработать способы преобразования отдельных
цифр и эффективного вычисления полинома.
Вычисление полинома. Известно несколько алгоритмов вычисле-
ния полинома, различающихся по своей эффективности. Наиболее
приемлемым (и естественным) представляется приведенный ниже
алгоритм, который позволяет вычислять и суммировать каждый
член полинома, начиная с низких степеней. Во избежание выполне-
ния лишних операций умножения требуемую степень х получают из
предыдущей степени. Предполагается, что коэффициенты находятся
в массиве с.
xpower := 1;
value := с[0];
for i := 1 to n do
begin
xpower := xpower*x;
value := value + c[ij»x
end
5*
131
Для сравнения эффективности этого алгоритма с другими необ-
ходимо как-то измерять объем вычислений. Можно, конечно,
запрограммировать каждый алгоритм на языке Ассемблера и подсчи-
тать число выполняемых команд, но тогда прцдется запрограмми-
ровать и несколько алгоритмов, которые не будут применяться.
Удобной мерой сравнения алгоритмов является число арифметиче-
ских операций, выполняемых над данными. Нетрудно определить,
что приведенный выше алгоритм вычисления полинома порядка
п требует 2п операций умножения и сложения.
Вычисление полинома по методу Горнера. Метод Горнера поз-
воляет значительно сократить число операций умножения, но при
прежнем числе операций сложения. Эффективность этого метода
обеспечивается вынесением общих членов полинома За скобки
(факторизацией). Достоинство факторизации видно даже на таком
простом примере:
x*y+x*z=x*(y+'i).
Вычисление выражения в первой форме требует выполнения двух
операций умножения и одной операции- сложения, а во второй фор-
ме — всего двух операций: умножения и сложения.
Рассмотрим полином
7х*+3^-Мх2+6х-9.
Вычисление его по первому способу потребует выполнения
восьми операций умножения. Но все члены полинома, кроме констан-
ты, кратны х, поэтому х можно вынести за скобки:
(7х3+Зх,-12х+6)*х-9.
Полином в круглых скобках можно вычислить по первому
способу, выполнив шесть операций умножения. Итак, в целом выпол-
нено семь операций умножения, т. е. одна операция умножения
исключена. Процесс вынесения за скобки можно продолжить и по-
лучить следующий окончательный результат:
((((7)*х-|-3)*х— 12)»х-|-6)»х—9.
Таким образом, для вычисления полинома достаточно четырех
операций умножения.
Общая форма факторизации по методу Горнера:
(... (с„) »х+с„_|) *х+ся_2)»х+ ... +с2) »Х+С|)*Х+Со.
Здесь для вычисления полинома степени п требуется выполнить
п операций умножения и п операций сложения. Число операций
умножения сократилось в два раза, а сложность алгоритма практи-
чески не увеличилась. По существу, этот алгоритм стал короче и
его можно программировать в виде очень простого цикла. При
каждом проходе по циклу вычисляется значение подвыражения
в следующей паре скобок (начиная с внутренних скобок):
132 .
value := c[n];
for i := n — 1 downto 0 do
value := ,valueox + c[i]’
Преобразование символьного кода по методу Горнера. Выше
получен эффективный алгоритм вычисления полиномов. Для преоб-
разования целого числа, представленного символьным кодом, в
число в дополнительном коде необходимо преобразовать отдельные
цифры dt, представленные кодом ASCII, в цифры в дополнительном
коде. Эта задача оказывается очень простой. Коды ASCII цифр
находятся в диапазоне от 48ю («О») до 57ю («9»), поэтому вычитание
48 из цифры в коде ASCII дает <чистую» цифру. (Коды цифр
естественнее выглядят в шестнадцатеричной системе: это 30—39.
Правые четыре бита являются двоичным представлением значения
цифры.)
Преобразование реализуется приводимым ниже сегментом прог-
раммы. Отметим, что используемый в программе алгоритм несколько
модифицирован для упрощения инициализации value. Эту модифи-
кацию вряд ли можно считать улучшением, так как программе
из-за нее приходится выполнить лишние операции сложения и умно-
жения.
MINUS - 45
LFTNBL - 48
| ФОРМУЛИРОВКА ЗАДАЧИ
Этот программный сегмент преобразует целое число /ио символьного кода
в дополнительный код (длинное слово), испольоуя вместо команд преоб-
разования VAX метод Горнера вычисления полинома.
Алгоритм вычисления!
value ! Oj
for 1 I" n downto О do
value « value * 10 * d(i)
где предполагается п+1 цифра d(n)d(n-1)...d(l)d(0).
Полагаем, что числовая символьная цепочка начинается в RECORD,
а длинное слово в дополнительном коде запоминается в NUMBER.
ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ R6 адрес цифры
R7 значение в ^дополнительном коде
R8 число цифр (счетчик цикла)
133
I
I
RIO преобраоование цифры
CLRL R7 1 Value i« 0
CLRL RIO 1 Сбросить цифру
MOVAB RECORD*1,R6 1 Адрес первой цифры
1
NEXTi MULL2 #10,R7 1 Value * 10
SUBB3 #LFTNBL,(R6)+,R(10) 1 Двоичное онемение цифры
ADDL2 R10,R7 1 Value * 10 * цифра
SOBGTR R8,NEXT
1
СМРВ RECORD,#MINUS 1 Проворить онак минус
BNEQ STORE 1 Минуса нет
MNEBL R7tR7 1 Иоменить онак
STORE1 MOVL R7,NUMBER 1 Запомнить число
Приведенный алгоритм моделирует действия команд CVTSP
и CVTPL. Рассмотрим некоторые задачи, реализуемые этими
командами. По команде CVTSP выполняются довольно рутинные
операции — выделяется младшая тетрада каждого байта, т. е.
двоичный эквивалент цифры, и две тетрады упаковываются в байт;
при этом не производятся никакие численные расчеты. По команде
CVTPL число в упакованном десятичном формате — цепочка цифр
в четырехбитовом представлении — преобразуется в число, представ-
ленное двоичным дополнительным кодом. Следовательно, действие
команды CVTPL подобно вычислению полинома, которое выполня-
лось выше по методу Горнера.
7.5. Заключение
Коды условий представляют собой четыре однобитовых флага
в PSW, которые устанавливаются большинством команд, чтобы
зафиксировать свойства одного или нескольких операндов. Эти
коды обозначаются символами N (отрицательное или меньшее зна-
чение), Z (нуль или равенство), V (переполнение) и С (перенос
или беззнаковое меньшее значение). Коды условий проверяются по
командам условных переходов (удовлетворяется ли заданное в них
условие). Если условие удовлетворяется, в PC загружается адрес
назначения перехода; в противном случае переход не выполняется.
Назначение перехода в командах условных переходов должно
находиться на расстоянии не более 127 байт от команды. Команды
безусловных переходов BRB и BRW отличаются друг от друга
134
тем, что расстояние назначения в команде BRW составляет 32767
байт, а в команде BRB — 127 байт.
Предусмотрены две команды, специально предназначенные для
проверки битовых флагов: с их помощью проверяется бит 0 длинного
слова и в зависимости от состояния этого бита выполняется
(или не выполняется) переход. Команды переходов приведены в
табл. 7.1.
Команды проверки и сравнения можно применять для явной
установки кодов условий. Это команды TSTx и СМРх (где х=В,
W или L) и СМРСЗ; последняя предназначена для сравнения
символьных цепочек с использованием регистров R0—R3.
При программировании рекомендуется привлекать как можно
меньше команд переходов .и обеспечивать ход выполнения программы
(т. е. определенную последовательность выполнения команд) в одном
направлении к нижнему концу страницы, исключая, конечно, циклы.
Команда JMP, как и BRB и BRW, изменяет последовательный
ход выполнения программы, поскольку в PC загружается адрес
назначения; здесь допускается почти любой режим адресации.
Команды управления циклами представлены в табл. 7.2. Все
они различаются степенью гибкости, а некоторые из них допускают
естественную реализацию распространенных операторов циклов в
языках высокого уровня.
Команды управления циклами
Таблица 7.2
Команды Действие Тип данных параметров цикла Смещение перехода1
SOBGTR индекс, назн. SOBGEQ AOBLEQ предел, индекс, назн. AOBLSS АСВх предел, инкр, индекс, назн. 1 Смещение перехода представляет собой р перехода. Если в команде смещение закодирован Вычесть 1; перейти, если>0, >0 Прибавить 1; перейти, если ^.предела < предела Инкремент индек- са; перейти, если не за пределом асстояние в байтах от т о байтом, расстояние дои L L х = В, W, L F, D, G или Н Инкремент может быть отрицатель- ным екущей команды imho быть от — В в W I до назначения 128 до 127 байт.
135
7.6. Упражнения
1. Предположим, что содержимое регистра R6 составляет 001A8EF2, а регистра
R10—FFFE90F2. Найдите значения кодов условий после выполнения каждой из
следующих команд:
a) CMPL R6, R10, б) СМРВ R6, R10,
в) CMPW RIO, R6, г) TSTB R10,
, д) TSTW R6.
2. Пусть регистр R7 содержит шестнадцатеричное значение 50FA0049, а регистр
R8 — FF0601F3. Найдите значения кодов условий после выполнения каждой из
следующих команд:
a) ADDL3 R7, R8, R9, 6)SUBL2 R8, R7, в) SUBL2 R8, R7,
г) MOVW R7, ALPHA, д) CVTWL R8, ВЕТА.
3. Какая команда выполняется следующей — по адресу HERE или THERE, если
до выполнения приведенных ниже команд содержимое R6 составляет FFEA609C,
а регистра R7—FFFF4079?
CMPL R6,R7
BBEQ THERE
HEREi
4. Напишите команды для перехода к метке NOTYET, если длинное слово
в ячейке TIME меньше или равно длинному слову в регистре R8.
5. Напишите команды, позволяющие проверить, содержит ли байт в ячейке CHAR
код ASCII цифры. Если это не так, необходимо выполнить переход к метке NOTD1G1T.
в. Напишите команды для замены целого числа (формат слова) в регистре R10
его абсолютным значением.
7. Несмотря на отсутствие кода условия Р, показывающего, что число —
положительное, можно проверить это по значениям N и Z. Можно ли определить,
что число является «отрицательным», «нулем» или «положительным», если коды N и
Z заменить кодами Р и Z; Р и N?
8. Предположим, что имеются массив TAGS байтов и массив NAMES символь-
ных цепочек, каждый элемент которого состоит из 24 символов. Элементы массива
TAGS ассоциируются с фамилиями в соответствующих позициях массива NAMES.
В длинном слове COUNT указано число элементов каждого массива. Напишите
команды для печати всех фамилий с ненулевыми значениями TAGS.
9. Напишите команды для перехода к метке SPECIAL, если байт в регистре R8
содержит значения 7, 11 или 13. В противном случае необходимо выполнять
следующую команду.
10. Напишите команды для перехода к метке PASS, если длинные
слова в ячейках ALPHA, ВЕТА и GAMMA больше 1024; в противном случае вы-
полняется следующая команда.
11. Напишите команды для перехода к метке THERE, если значение слова
в ячейке WORD находится между —100 и 100, а байт в ячейке BYTE
равен 3 или 4.
12. Напишите команду (или команды) для перехода к метке ODD, если
целое число в слове COUNT нечетное. (Целое число не обязательно является
положительным.)
13. Предположим, что ALPHA и ВЕТА — адреса двух целых чисел в формате
тетраслова. Напишите последовательность команд для перехода к метке АВС,
если тетраслово в ALPHA больше тетраслова в ВЕТА.
14. Пользуясь, табл. 7.1, найдите пары команд условных переходов
(кроме BEQL и B^QLU), которые осуществляют переход при одних и тех
же условиях.
136
15. Напишите программу нахождения наибольшего элемента в массиве DATA,
слов и запоминания его в слове LARGEST. Длинное слово NUM содержит
число элементов в массиве.
16. Предположим, что в массиве DATA длинных слов хранятся целые числа,
значения которых варьируются в очень широком диапазоне. Пусть BYTES,
WORDS и LONGS — три пустых массива, соответственно байтов, слов и длинных
слов. Напишите команды для сортировки чисел из массива DATA по трем массивам,
чтобы каждое число занимало минимальную единицу памяти, в которую
оно помещается. Считайте, что в длинном слове NUM указано число
элементов массива DATA. Необходимо запомнить в длинных словах NUM______BYTES,
NUM____WORDS и NUM______LONGS сколько чисел попало в каждый из трех массивов.
17. Напишите команды для ввода с терминала последовательности строк
переменной длины и размещения их в памяти по адресу TEXT в формате,
описанном в упр. 27 гл. 6. Считайте, что строка длиной нуль означает конец
ввода и для отметки конца текста в память необходимо записать нулевой байт.
18. Предполагая формат хранения текста, описанный в упр. 27 гл. 6 и преды-
дущем упражнении, напишите команды для печати всех строк текста.
19. Проанализируйте следующий сегмент программы. Его действия не совсем
совпадают с тем, что написано в заголовке. Найдите и исправьте ошибку.
I Этот программный сегмент отыскивает о символьной цепочке первый
; пробел.
1
; ДАННЫЕ
I CHARS символьная цепочка
I LENGTH длина цепочки (длинное слово)
I LOCBLANK адрес первого пробела
$
; Если пробелов в цепочке нет, в LOCBLANK запоминается О.
$ Использование регистров! R6 указатель текущего символа
$ R7 счетчик цикла
1
MOVL LENGTH,R7 1 Инициализировать счетчик цикла
BEQL NOTFND 1 Перейти, если длина О
1 MOVAB CHARS,R6 1 Установить указатель
TEST! CMPB 1ИА/ /,(R6)+ 1 Проворить на пробел, инкремент указателя
BEQL FOUND 1 Перейти, если пробел
SOBGTR R7,TEST 1 Управление циклом
NOTFNDi CLRL R6 1 Пробел но найден
FOUND! MOVL R6,LOCBLANK 1 Запомнить адрес пробела
20. Правильно ли составлен алгоритм двоичного поиска на языке высокого
уровня, если таблица пустая, т. е. если пит = 01 Правильно ли выполняется в этом
случае программа, показанная на рис. 7.6?
21. Пользуясь данными, приведенными на рис. 7.6, но считая, что таблица
может быть не рассортированной, напишите программу последовательного
(не двоичного) поиска индекса элемента, содержащего ключ из ячейки KEY.
137
- 22. Укажите все изменения в программе двоичного поиска на рис. 7.6 для случая,
когда ключами являются символьные цепочки длиной 6, а не слова.
23/Укажите все изменения в программе двоичного поиска для запоминания
адреса (а не индекса) отыскиваемого элемента. (Как и раньше, запомните О,
если элемента в таблице нет.)
24. Суть проблемы заключается в модификации программы двоичного поиска
для случая, когда в таблице может быть несколько элементов с одним и тем же
ключом. Укажите, какие изменения потребуются в программе для эффективного
поиска всех элементов, содержащих отыскиваемый ключ. Запомните индекс первого
элемента с искомым ключом в длинном слове INDEX, а число таких эле-
ментов — в слове NUM__COPIES. (Если ключ не найден, запомните в ячейках
INDEX и NUM-COPIES нули.)
25. Укажите изменения, которые необходимо сделать в программе двоичного
поиска для таблиц, содержащих в каждом элементе 32 байта.
26. При последовательном и двоичном поиске (упр. 21 и рис. 7.6)
число выполнений команд в цикле поиска зависит от того, в каком
месте таблицы находится отыскиваемый элемент. Предположим, что таблица содержит
127 элементов. Определите, сколько раз необходимо выполнить команды в цикле
для каждого алгоритма, если отыскиваемый элемент находится: а) в 64-й позиции,
б) его в таблице нет, в) в первой позиции, г) в 96-й позиции, д) в последней позиции.
27. Предположим, что команда AOBLSS отсутствует. Напишите эффективную
последовательность команд для замены команды AOBLSS RIO, R6, LOOP.
28. Для случая, когда нет команды ACBW, напишите эффективную последо-
вательность команд, позволяющую заменить одну команду \
ACBW CNT,#7, R9, CHECK
29. Пусть команды в цикле должны выполняться п раз, где п — длинное слово
в регистре R9, которое может быть равно нулю. Напишите команды инициализации
и управления циклом для выполнения указанных действий с привлечением мини-
мального числа наиболее простых команд.
30. В языке Алгол оператор for имеет следующий общий формат:
1ог<индекс>: = <HA4_3HA4EHHE>step<HH/(PEMEHT>until<
<{7РЕДЕЛ><1о
<ТЕЛО ЦИКЛА>
Если инкремент, определенный после step, положителен, тело цикла выполняется
для* каждого значения индекса, начинающегося с <нач_значение^ и увеличи-
вающегося на <инкремент>, до (но не включая) предела, специфицированного
после until. Если же инкремент отрицателен, тело цикла выполняется для умень-
шающихся значений до (но не включая) указанного предела. Пользуясь
подходящими командами системы VAX, напишите все команды, необходимые для ини-
циализации и управления циклами, которые описываются следующими операторами
языка Алгол:
a) for I: = 2*(N + 4)step 12 until LEVEL2 do
<тело цикла>
б) for CNTR: = STARTstep SIZE until LIMIT do
<тело цикла>
(SIZE может быть положительным или отрицательным).
31. Алгоритм так называемой «пузырьковой» сортировки упорядочивает список
в неубывающем порядке следующим образом. С начала списка сравниваются все
138
соседние пары элементов (первый и второй, затем второй и третий, третий и четвертый
и т. д.) и производится обмен элементов пары, если они не соот-
ветствуют принятому порядку. После первой обработки список может быть
рассортирован не полностью, но наибольший элемент окажется в последней позиции —
где он и должен быть. Процесс сравнения соседних пар, а при необходимости
и обмена элементов, повторяется для всех элементов, кроме последнего; при этом
всякий раз на своем месте оказывается элемент, ближайший к наибольше-
му. Этот процесс продолжается до тех пор, пока не будет упорядочен весь список.
Описанный алгоритм можно усовершенствовать, если отмечать позицию
последнего обмена элементов. Если, например, последний обмен в одном проходе по.
списку состоялся в z-й позиции, то все элементы от (i+ 1)-й позиции до последней
находятся на правильных местах и их проверять вновь не нужно.
В алгоритме пузырьковой сортировки, написанном на языке Паскаль, использова-
ны следующие обозначения: last — индекс последнего элемента, который может
быть размещен не по порядку; pairs — число сравниваемых пар; пит — число
элементов в массиве list. Отметим, что last устанавливается в нуль
перед каждым проходом по списку, чтобы зафиксировать случай, когда не совер-
шено ни одного обмена, и список, следовательно, рассортирован.
last := num;
while last > 0 do
begin
pairs := last — 1;
last := 0;
for j := 1 to pairs dp
if list[ j] > list[ j+1] then begin
temp := list[ j];
list[j] := list[j+l];
list[j+l] := temp;
last :=j
end
end
Пользуясь этим алгоритмом как руководством, напишите ассемблерную
программу для пузырьковой сортировки.
32. Предположим, что регистры R6 и R7 хранят адреса первого и
последнего байтов символьной цепочки, содержащей целые числа в символьном коде.
Числа размещены в свободном формате, т. е. они могут быть в любых местах
цепочки, но разделяются минимум одним пробелом. (Считайте, что первый символ
в цепочке является знаком или пробелом.) Напишите команды для: а) нахождения
первого числа в цепочке, б) преобразования его в длинное слово, содержащееся
в регистре R9 и представленное в дополнительном коде, в) формирования в регистре
R6 адреса байта в цепочке, который находится за найденным числом; другими
словами, регистр R6 указывает на байт, с которого должен начинаться поиск
следующего числа, г) загрузки единицы в регистр R0, если число найдено и
преобразовано правильно; загрузки нуля в регистр R0, если число в цепочке
не обнаружено.
139
33. При обработке входной записи не всегда можно предполагать, что она
представлена точно в такой форме, которую ожидает программа. Такая ситуация
вполне допустима, если ввод данных с терминала осуществляется человеком,
так как при печати возможны ошибки или тот, кто выполняет ввод, не знает
правильного формата. Проанализируйте задачу из упр. 32 и составьте список
всех возможных ошибок во входной записи. Для каждого типа ошибок покажите,
легко или трудно программе проконтролировать ошибку и выдать соответствующее
сообщение на терминал.
34. Пусть й память, начиная с ячейки RECORD, только что считана входная ,
запись, содержащая предположительно пять целых чисел в свободном формате.
Считая, что уже имеется программа, удовлетворяющая спецификациям упр. 32,
напишите все дополнительные команды, которые потребуются этой программе в
цикле для преобразования пяти целых чисел и запоминания результатов в массиве
ARRAY длинных слов. Если во входной записи окажется менее пяти целых чисел,
на терминал следует выдать сообщение об ошибке.
Г лава 8
Форматы машинного кода,
трансляция и выполнение программы
8.1. Общие замечания
Форматы программ в машинном коде. В настоящей главе мы
рассмотрим, как кодируются машинные команды и как они интерпре-
тируются и выполняются центральным процессором, а также каким
образом ассемблер транслирует спецификаторы операндов ассемб-
лерных операторов в машинный код. Прежде всего обсудим
простые режимы адресации, описанные в гл. 4, а затем уже
перейдем к более сложным режимам.
В современных ЭВМ команды представляются в машинном коде,
состоящем из кода операции, т. е. двоичного набора, определяющего
выполняемую операцию, и спецификаторов операндов. Варианты
кодирования операндов зависят от архитектуры и системы команды
ЭВМ, однако их можно разделить на две большие группы: с фик-
сированными и с гибкими форматами. В первой группе допускает-
ся несколько различных форматов, но каждая команда всегда
кодируется только в одном из них. Примерами ЭВМ такого типа
служат IBM 360 и IBM 370. Во второй группе для кодирования от-
дельных операндов предусмотрен целый набор форматов, называе-
мых режимами адресации, и, за некоторыми исключениями, для обра-
зования команды любой режим адресации возможно объединить с
любым кодом операции. Система VAX относится именно к этой
группе. В ЭВМ с гибким форматом кодирования операндов
режимы адресации несколько варьируются по длине и способу
доступа к операнду. Следовательно, выбор правильного режима
влияет на эффективность машинного кода по объему используемой
памяти и времени выполнения.
Отметим, что термин режим адресации обычно подразумевает
способ определения операнда в машинном коде. В гл. 4 этот же
термин использовался при описании способа спецификации операн-
дов в исходных ассемблерных операторах. Читатель должен
141
Старшие
адреса
Младшие
адреса
Спецификатор последнего операнда . . . Спецификатор первого операнда Код операции
Один или несколько Один или несколько 1 — 2 байта
байт байт
Спецификатор операнда (кроме режима смещения)
Ст. адрес — Мл. адрес
Дополнительная информация Байт режима
О или более байт Один байт
Байт режима (кроме литерального режима)
7 4 3 О
Номер режима Номер регистра
Рис. 8.1. Общий формат команд в машинном коде
понимать из контекста, когда речь идет о форматах исходных опера-
торов, а когда о форматах машинного кода.
Общий формат команд VAX в машинном коде представлен на
рис. 8.1. Первый байт (с меньшим адресом) показан справа в
соответствии с распечаткой машинного кода в листингах ассемблер-
ных программ. Команды могут начинаться на любой байтовой
границе с кода операции, а за ним следуют спецификаторы операн-
дов, число которых зависит от функций команды (максимум шесть).
Порядок размещения спецйфикаторов операндов соответствует по-
рядку следования их в исходном ассемблерном операторе. Коды
операций в стандартной системе команд имеют длину в один
байт. (Кроме стандартных, предусмотрены необязательные типы дан-
ных с плавающей точкой, команды манипуляций которыми имеют
двухбайтовые коды операций. В дальнейшем систему команд можно
будет расширить, включив в нее большее число команд с двухбай-
товыми кодами операций.) Каждый спецификатор операнда, за ис-
ключением режима перехода, содержит номер конкретного режима
адресации и некоторую дополнительную информацию об этом режи-
ме. Режим перехода должен использоваться в командах переходов,
поэтому он не требует номера режима. Первый байт спецификато-
ра операнда в большинстве режимов называется байтом режима
и включает номер режима в битах 7:4 и номер регистра в битах 3:0.
Процессор по номеру режима определяет, сколько дополнительных
байтов относится к текущему операнду. Если, например, задан
регистровый режим, в спецификаторе операнда указан номер режима
142
Исходный оператор
CLRW —(R5)
Машинный код
Первый байт
Код операции
CLRW«B4
Спецификатор операнда
Режим » 7, Rn s R5
Первый байт
ADDL2 (R10)+, R7
Спецификатор
Спецификатор
первого операнда Код операции
Режим 8, Rn RIO ADDL2 » СО
второго операнда
Режим 5, Rn s R7
Рис. 8.2. Примеры машинного кода
5 и номер регистра, в. этом случае дополнительные байты для
режима 5 не нужны.
На рис. 8.2 показано кодирование некоторых команд с простыми
режимами адресации. В спецификаторы операндов входят только
номера режима и регистра. Напомним, что без дополнительных
указаний машинный код всегда дается в шестнадцатеричном
представлении. Позднее мы рассмотрим более длинные и сложные
примеры.
Выполнение команды. Рассмотрим кратко выполнение команды.
Программный счетчик PC в составе центрального процессора CPU
предназначен для «слежения» за командами во время их выполнения.
Когда CPU готов приступить к выполнению новой команды, он
выбирает адресуемый PC байт и производит инкремент (PC) на
единицу. CPU интерпретирует считанный байт как код операции
и определяет из него, что это за команда и сколько в нее входит
операндов*. Для каждого операнда, кроме операндов режима перехо-
да, CPU сначала выбирает адресуемый PC байт1 2 (байт режима),
производит инкремент PC на единицу и определяет по номеру режима
число байтов с дополнительной информацией. Затем он выбирает
эти байты, начиная с содержащегося в PC адреса, и вновь производит
соответствующий инкремент PC. Проанализировав таким образом
весь спецификатор операнда, CPU вычисляет адрес последнего
и корректирует при необходимости содержимое используемого ре-
1 Команды с необязательными типами данных О иН имеют двухбайтовые коды
операций. Первый байт находится в диапазоне FC— FF, и CPU может определить,
что следующий байт является частью кода операции. В настоящей главе для
простоты рассматриваются только однобайтовые коды операций.
2 Фактически CPU не выбирает команды из памяти байт за байтом; для эконо-
мии времени он сразу выбирает несколько байтов и хранит их в буфере команд.
143
гистра (например, при задании автоинкрементного или автодекре-
ментного режимов). После обработки спецификаторов всех операн-
дов выполняется собственно операция.'Заметим, что при этом в PC
находится адрес следующей команды.
Функции ассемблера. Основное назначение ассемблера — тран-
слировать исходные программы с языка Ассемблера на машинный
язык. Ниже дается краткое и несколько упрощенное описание
функций ассемблера, чтобы читателю было понятно, как он трансли-
рует рассматриваемые в настоящей главе спецификаторы операндов.
Остальные аспекты процесса ассемблирования обсуждаются в гл. 10.
Необходимо четко представлять себе различие между временем
ассемблирования и временем выполнения. Все реализуемые CPU
действия по выборке, интерпретации и выполнению команд произво-
дятся во время выполнения после того, как ассемблер (и редактор
связей) завершил свои операции. Прежде всего осуществляется
ассемблирование — трансляция исходной программы. Однако здесь
мы часто будем рассматривать первым процесс выполнения, а затем
уже процесс трансляции. Такой порядок рассмотрения объясняется
тем, что форматы машинного кода и способы интерпретации и
выполнения команд в CPU создают основу для функционирования
любой ЭВМ. Ассемблирование отодвигается на второй план как реа-
лизация конкретной задачи, определяемой архитектурой и системой
команд ЭВМ.
Для каждой ЭВМ обычно разрабатывается несколько ассембле-
ров, но мы приведем достаточно общее описание, применимое ко
многим из них.
Ассемблер осуществляет два прохода по транслируемому моду-
лю. Главная цель первого прохода — найти все символические имена
и определить их значения (если это возможно). Ассемблер форми-
рует так называемую таблицу имен, в которую включены имена,
их значения и некоторая дополнительная информация. Большинство
символических имен представляют собой метки команд и областей
данных. Как показано в гл. 4, ассемблер использует свой счетчик
ячеек, показывающий, сколько байтов уже распределено для команд
и данных, поэтому он может вычислить адрес отмеченного именем
байта. Поскольку ассемблер «не знает», где фактически при выполне-
нии будет размещаться программа в памяти, он инициализирует
счетчик ячеек на нуль. Поэтому значения в таблице имен вычисляют-
ся относительно начальной точки ассемблера. Для правильного
вычисления значений имен важно, чтобы ассемблер мог определить
при первом проходе число байтов, требуемое для кодирования
каждой команды.
При втором проходе по исходной программе ассемблер генери-
’ рует машинный код. Преобразование мнемоник команд в коды
операций осуществляется с помощью встроенной таблицы мнемоник.
Спецификаторы операндов, не содержащие имен, кодируются оче-
144
видным образом. Если же в них фигурируют имена, ассемблер
просматривает таблицу имен и соответствующим способом кодирует
спецификаторы операндов.
8.2. Некоторые
регистровые режимы
Рассмотрим реализацию регистровых режимов адресации, вве
денных в гл. 4, а также один новый режим — режим смещения
Простые регистровые режимы. Первые четыре режима целесооб
разно объединить в одну группу, поскольку их форматы очень схожи
Режим Номер режима
Регистровый 5
Регистровый 6
косвенный
Автодекрементный 7
Автоинкрементный 8
Ассемблерный формат
(R/i=R0...... Rl 1, АР, FP, SP)
Rn
(Rn)
— (Rn)
(Rn) +
Во всех четырех режимах спецификатор операнда содержит толь-
ко байт режима; биты 7:4 определяют номер режима, а биты 3:0—
номер регистра. (Напомним, что AP = R12, a FP и SP кодируются
числами 13 и 14.)
Например, 64 является спецификатором операнда (R4), т. е.
регистрового косвенного режима с использованием' R4. Именно
регистровые режимы показаны на рис. 8.2.
Поскольку эти режимы были описаны в гл. 4 и их реализация
довольно проста, мы не будем давать здесь отдельно примеры, а
включим их в другие примеры, приведенные ниже.
Трансляция исходного оператора с любым из перечисленных выше
режимов для ассемблера является несложной задачей; режим и но-
мер регистра специфицированы в формате языка Ассемблера.
Режим смещения. В режиме смещения адрес операнда равен
сумме числа, называемого смещением, и содержимого регистра.
Простейший ассемблерный формат режима смещения имеет вид:
смещение (Rn)
где Rn = R0,..., Rll, АР, FP или SP. Процессор обрабатывает
спецификатор операнда следующим образом:
адрес операнда = (Rn) + смещение
Смещение может быть выражением или (довольно часто)
десятичным числом, положительным или отрицательным, которое
хранится в команде как целое число в дополнительном коде. Су-
ществуют три режима смещения, различаемые подлине смещения,—
байт, слово и длинное слово. Форматы спецификатора операнда
иллюстрируются рис. 8.3. Программист может явно, задать режим
или предоставить решение о его выборе ассемблеру. Для задания
режима перед смещением указывается тип смещения и знак вставки.
145
Режим
Номер режиме
(16-ричный)
Формат
(п = 0, 1.15)
Смещение - байт
Смещение — слово
Смещение — длинное слово
Рис. 8.3. Режим смещения. Регистры А'Р, FP и SP кодируются как 12, 13 и 14.
Если п=15 (для PC), режим называется относительным
Ассемблерные форматы режимов смещения:
ВЛ смещ (Rn) режим А, смешение — байт,
W* смещ (Rn) режим С, смещение — слово,
ЬЛ смещ (Rn) режим Е, смещение.— длинное слово.
Если выбор режима предоставлен ассемблеру, он попытается
использовать минимум памяти. Ассемблер должен определить число
байтов для смещения уже при первом проходе, чтобы правильно
скорректировать счетчик ячеек. В том случае, когда нет достаточной
информации для определения требуемого числа байтов, он выбира-
ет два байта (режим С). Если позднее окажется, что на самом
деле требовалось четыре байта, редактор связей выдает сообщение
об ошибке.
Позже мы рассмотрим ситуацию, в которой режим смещения
особенно удобен, а пока приведем пример выполнения команды в
центральном процессоре.
Пример 8.1. Режим смещения. Предположим, что значение PC составляет 24Е,
процессор готов начать выполнение следующей команды и содержимое памяти
по адресу 24Е показано ниже. (Байт с наименьшим адресом находится справа—
именно так машинный код отображается в листингах ассемблерных программ.)
Проследим за действиями CPU и покажем адресуемый PC байт, когда CPU выби-
рает часть команды. Байт или байты, интерпретируемые на каждом этапе, подчеркну-
ты.
(РС)=24Е
... D6 87 01 2А С9 D0...
Производятся выборка адресуемого PC байта и инкремент (PC) на единицу:
(PC) = 24F
... D6 87 01 2А С9 DO...
Выбранный байт D0 интерпретируется как код операции. Байт D0 есть код
операции команды MOVL, требующей два операнда.
Осуществляются выборка байта, адресуемого PC, и инкремент (PC) на еди-
ницу:
(PC) =250
... D6 87 01 2А С9 D0
146
Интерпретация выбранного байта С9 как байта режима первого операнда.
Номер режима — С (режим смещения и размер смещения — слово). Используется
регистр R9. Будем считать, что (R9) = 408.
Так как смещение задано словом, выбирается адресуемое PC слово (два
байта) и производится инкремент (PC) на два:
(PC) = 252
... D6 87 01 2А С9 D0
Смещение равно 012А. Оно суммируется с содержимым регистра R9, и образу-
ется адрес первого операнда. Содержимое R9 не изменяется, а сложение осуществля-
ется в рабочих регистрах CPU. Адрес первого операнда равен: 408+12А = 532.
Следующий байт интерпретируется как байт режима второго операнда. После
его выборки производится инкремент (PC) на единицу:
(PC) =253
... D6_87 01 2А С9 DO
Байт режима второго операнда содержит число 87. Он определяет автоинкре-
ментный режим 8 и регистр R7. Для автоинкрементного режима дополнительные
байты не требуются. Адрес второго операнда содержится в регистре R7.
Поскольку типом данных команды MOVL является длинное слово, осуществля-
ется инкремент адреса в R7 на четыре.
Выполняется собственно операция — длинное слово, начинающееся с байта 532,
копируется в длинное слово, определяемое адресом второго операнда (число в регистре
R7 до его инкремента).
Рассмотренная команда в ассемблерной программе может иметь следующий вид:
MOVL 298(R9), (R7) +
Отметим, что смещение записано как десятичное число; ассемблер преобразует
его в дополнительный код.
Теперь CPU готов выполнять очередную команду. Подчеркнем, что PC адресует
байт в памяти за текущей командой. Именно этот байт будет интерпретироваться
как код операции следующей команды.
Применительно к режиму смещения важно отметить, что в адрес-
ных вычислениях используются 32 бита, поэтому при необходимости
смещение расширяется со знаком, и содержимое регистра не изменя-
ется. Если, например, смещение равно FF63 и регистр содержит
000045С2, адрес операнда вычисляется таким образом:
(Rn): 000045С2
смещение: + FFFFFF63 (расширение знака)
адрес операнда: 00004525
Содержимое регистра остается равным 000045С2.
Программирование с использованием режима смещения. Режим
смещения особенно удобен при обращении к отдельным данным
внутри большого блока взаимосвязанных данных, представленных
в некотором стандартном формате. Предположим, что блок памяти
содержит информацию о заданиях, подлежащих выполнению на
ЭВМ. Информация о каждом задании, называемая записью, пред-
ставлена в формате, показанном на рис. 8.4. При обработке каждой
записи (или элемента) целесообразно привлечь один из регистров,
например R10, в качестве указателя элемента (т. е. его первого
байта), а для адресации различных полей записи, воспользоваться
смещениями от содержимого R10. Поля на рис. 8.4 адресуются сле-
дующим образом:
147
имя задания
фамилия пользователя
время поступления
приоритет
блоки
флаги
0(R10)
8(R10)
16(R10)
24(R10)
25(R10)
26(R10)
или (RIO)
Основные преимущества применения режима смещения заключа-
ются в том, что он обеспечивает наглядный способ доступа к
полям записи, позволяет сэкономить время и уменьшает вероятность
ошибок при программировании. Допустим, нужно напечатать
список всех заданий с приоритетом 6 и выше, которые требуют не
менее 100 блоков дисковой памяти. Если в R10 содержится адрес
Рис. 8.4. Формат описания задания
Предполагается, что в списке подлежащих
выполнению заданий для каждого задания
включен показанный ниже блок информа-
ции. Поле BLOCKS содержит число необ-
ходимых заданию дисковых блоков, а поле
RESOURCE — флаг, показывающий по-
требность в различных системных ресур-
сах. Назначение остальных полей очевидно
из их названий
JOB NAME
(имя задания)
USER NAME
(имя пользователя)
TIME IN
(время поступления)
BLOCKS PRIORITY
(блоков) (приоритет)
RESOURCE FLAGS
(флажки ресурсов)
Другая информация
8 байт
8 байт
8 байт
по одному байту
2 байта
20 байт
записи, необходимо выполнить следующие действия, адресуя поля
соответствующим образом:
проверить приоритет 24(R10)
если меньше 6, перейти к NEXT
проверить требуемые блоки 25(R10)
• < если меньше 100, перейти к NEXT
напечатать имя задания (R10)
NEXT: установить R10 на адрес следующей записи ADDL2#48,R10
Применение регистровой косвенной адресации для каждого поля
связано с увеличением числа команд. Кроме того, придется либо
использовать больше регистров, либо модифицировать содержимое
R10, что затрудняет его идентификацию из-за условных переходов.
Четкость программы, особенно на языке Ассемблера, играет важ-
нейшую роль в уменьшении числа ошибок. Имеет смысл закрепить
регистр как фиксированную отсчетную точку для каждой записи,
а для обеспечения доступа к полям в записи выбрать режим
смещения.
Чтобы программа была читабельной, желательно ввести для
смещений символические имена и присвоить им значения с помощью
операторов прямого присваивания. Так, применительно к рассмотрен-
ному выше примеру печати заданий можно ввести следующие
определения:
148
JOB.NAME - О
USER.NAME - 8
TIME-IN » 16
PRIORITY » 24
BLOCKS - 25
RESOURCE.FLASS - 26
После этого обращения к полям записей принимают вид USER—
NAME(RIO), PRIORITY(RIQ) и т. д. Когда ассемблер транслирует
подобные операнды, он просматривает таблицу имен, подставляет
значения имен и далее действует так, как будто программист запи-
сал в командах численные смещения.
8.3. Литеральный режим
Литеральный режим применяется для кодирования небольших
неотрицательных операндов, которые определяются в самой команде,
а не в регистре или ячейке памяти. Ассемблерный формат литераль-
ного режима:
^выражение
Любой номер режима с двумя нулевыми битами слева (т. е.
режимов 0—3) является номером литерального режима; правые два
бита номера режима фактически служат частью значения литерала.
Поэтому спецификатор операнда имеет следующую форму:
00 литерал
Таким образом, целочисленный литерал может принимать 'зна-
чения от 0 до 63. Кодирование команды с литеральным операндом
показано на рис. 8.5. (Литералы с плавающей точкой рассмотрены
в гл. 13.)
Исходный оператор Машинный код
149
Когда CPU при выполнении команды встречает литеральный
операнд, он расширяет шестибитовое данное до размера, требуемо-
го командой. Например, литерал на рис. 8.5 будет расширен
(в рабочей зоне CPU) до 16 бит путем добавления слева нулей,
так как команда MOVW пересылает слово.
Функции ассемблера. Ассемблер при первом проходе должен опре-
делить, как закодировать операнд, начинающийся с символа ф,—
в литеральном или непосредственном режимах. Если операндом
является целое число в диапазоне от 0 до 63 (или число с плаваю-
щей точкой, которое помещается в шести битах), выбирается лите-
ральный режим. Если же операнд находится вне диапазона для
литералов или если ассемблер не может определить его значение
при первом проходе, операнд будет закодирован в непосредствен-
ном режиме. Примеры кодирования операндов приводятся в разд.
8.5 после рассмотрения непосредственного режима.
Программист может явно задать литеральный режим, поместив
перед операндом символы SA. Формат операнда принимает вид:
S'# выражение
8.4. Режим перехода
Режим перехода применяется для кодирования адреса назначения
перехода во всех командах передачи управления, включая команды
управления циклами. Так как режим перехода служит для назначе-
ния перехода в этих командах и может использоваться только
для их операндов, байт режима в спецификаторе операнда не
требуется. Назначение перехода всегда кодируется как смещение
от содержимого PC. Следовательно, если переход предпринимается,
CPU вычисляет адрес назначения:
назначение = (PC) 4-смещение
и загружает его в PC.
Смещение может быть положительным или отрицательным и
записывается как целое число в дополнительном коде, занимая
байт или слово в зависимости от конкретной команды. В команде
безусловного перехода BRB, во всех командах условных переходов
и в некоторых командах управления циклами размер смещения
равен байту. В команде безусловного перехода BRW и в некоторых
командах управления циклами размер смещения — слово.
Пример 8.2. Режим перехода. На рис. 8.6 показано кодирование команды
SOBGTR. Чтобы уяснить, каким образом CPU вычисляет адрес перехода, подроб-
но рассмотрим выполнение этой команды. Предположим, что команда начинается
с байта 635:
(PC) =635
... В6 D2 58 F5...
Производятся выборка кода операции и инкремент (PC) на единицу:
(PC) =636
... В6 D2 58 F5
150
Исходный оператор
SOBGTR R8, LOOP
Машинный код
(полагаем LOOP = 60А)
Байт
второго операнда. Байт режима для Код операции
Смещение перехода индекса SOBGTR
Рис. 8.6. Пример режима перехода. Для операнда с режимом
перехода байт режима отсутствует
Байт F5 представляет собой код операции команды SOBGTR. В ней должны
быть два операнда — индекс цикла и адрес перехода.
Осуществляются выборка байта режима "для получения индекса и инкремент
(PC) на единицу.
(PC) =637
...В6 D2 58 F5...
Байт режима 58 показывает, что номер режима — 5 (регистровый) и номер
регистра — 8, поэтому индекс содержится в регистре R8.
Вторым операндом является адрес перехода. Так как в команде SOBGTR
для назначения перехода всегда есть смещение' размером в байт, CPU выбирает
следующий байт, производит инкремент (PC) на единицу и интерпретирует
этот байт как смещение:
(PC) =638
... В6 D2 58 F5...
Далее для выполнения операции, специфицированной в команде SOBGTR,
из содержимого R8 вычитается единица (значение (R8) изменяется) и определя-
ется, необходимо ли осуществлять переход.
Если (R8)^0, выполнение команды завершается и CPU переходит к интерпре-
тации и выполнению команды по адресу 638. Если же (R8)>0, требуется переход,
для чего смещение D2 прибавляется к значению (PC).
Адресные вычисления всегда ведутся с 32 битами, поэтому
текущее значение (PC): 00000638
+ смещение: + FFFFFFD2 (расширение знака)
новое значение (PC): 0000060А
На этом выполнение команды заканчивается. Переход считается реализованным,
когда CPU начинает обработку следующей команды, так как он определяет ее
местонахождение по (PC).
Отметим, что при рыборке смещения был произведен инкремент
PC; поэтому адрес перехода равен сумме смещения и адреса команды,
следующей за командой перехода. Поскольку целое число в байте
находится в диапазоне от —128 до 127, команды переходов, где
размер смещения составляет 1 байт,- можно использовать только
для перехода к командам, находящимся в указанном диапазоне.
Почему в режиме перехода используется смещение от (РС)?
В ассемблерном операторе назначение перехода обычно определяется
именем, являющимся меткой целевой команды (к которой осуще-
151
ствляется переход). Почему этот адрес закодирован как смещение
от содержимого PC, а не просто как значение имени, т. е. как адрес
целевой команды? Напомним, что значение имени, хранимое в табли-
це имен ассемблера, представляет собой адрес отмеченной команды
относительно начала модуля или программной секции. Модуль
связывается с другими модулями й при каждом выполнении загру-
жается в различные области памяти. Фактический (виртуальный)
адрес перехода ассемблеру не известен, но при выполнении команды
CPU должен вычислить именно этот фактический адрес перехода.
Расстояние между адресом назначения перехода и содержимым
PC (адрес команды, следующей за командой перехрда) постоянно;
число байтов, требуемых для кодирования команд, находящихся
между командами перехода и целевой, не зависит от размещения
программы в памяти. Таким образом, с помощью нескольких режи-
мов адресации ассемблер может закодировать адресные выражения
как смещения от содержимого PC. При использовании смещений
программа будет правильно выполняться в любой области памяти,
и не понадобится коррекция спецификаторов операндов в момент
ее загрузки. Такой способ трансляции адресных выражений предус-
мотрен во всех современных ЭВМ.
Функции ассемблера. Рассмотрим оператор на рис. 8.6:
SOBGTR R8, LOOP
Предположим, что значение счетчика ячеек . = 235, когда ассемб-
лер встречает этот оператор при первом проходе. (Напомним,
что счетчик ячеек обозначен точкой.) Ассемблер определяет, что
машинный код для этого оператора потребует три байта: для кода
операции, для спецификатора первого операнда (так как в нем
указан регистровый режим) и для сйецификатора второго операнда
(поскольку в команде SOBGTR смещение всегда имеет размер в
1 байт). Далее ассемблер осуществляет инкремент счетчика ячеек
на три и переходит к следующей команде. Когда же ассемблеру
встречается эта же команда при втором проходе, осуществляется
собственно трансляция. Мнемоника SOBGTR кодируется как F5, а
регистр R8 кай 58; для каждого байта производится инкремент
счетчика ячеек. Если ассемблер вычисляет смещение до адреса пере-
хода, содержимое счетчика ячеек составляет 237 (или . = 237).
Для вычисления смещения ассемблер отыскивает метку LOOP в
таблице имен и определяет, например, что LOOP = 20A. Он «знает»,
что при выполнении команды CPU реализует соотношение:
назначение = (PC) 4-смещение
Отсюда находится смещение:
LOOP = адрес следующего байта 4-смещение
20А = 238 4- смещение
смещение = 20А—238 = FD2
Таким образом, спецификатор третьего операнда транслируется
как D2. Отметим, что на рис. 8.6 команда находится по адресу
152
635, а не 235, а команда с меткой LOOP — по адресу 60А, а не 20А.
Здесь предполагается, что модуль с командой SOBGTR загружается
в память, начиная с байта 400. Ассемблеру это не известно, но
вычисленное им смещение будет правильным, независимо от раз-
мещения программы в памяти.
Если вычисленное ассемблером смещение оказывается слишком
большим для байта (т. е. выходит'за пределы диапазона —128... 127),
ассемблер формирует сообщение об ошибке.
8.5. Относительный
и непосредственный режимы
Выше неоднократно подчеркивалось, что CPU с помощью PC
отслеживает местонахождение выполняемой команды в командном
потоке. Но ведь PC — это регистр. Что произойдет, если использовать
его в качестве спецификатора операнда, как и регистры R0, ..., R11,
АР, FP и SP? В некоторых режимах адресации подобное применение
PC приводит к нежелательным последствиям и потому просто запре-
щено. Однако в других режимах PC как спецификатор операнда весь-
ма удобен. Режимы адресации с PC существенно отличаются от режи-
мов адресации с другими регистрами, что подчеркивается их назва-
ниями. Относительный и непосредственный режимы, введенные
в гл. 4, представляют собой частные случаи режимов смещения и ав-
тоинкрементного с использованием PC.
Относительный режим. Адрес операнда, определенный в исход-
ном операторе именем или, в более общем случае, выражением,
является операндом относительного режима. В машинном коде опе-
ранд относительного режима представляется в режиме смещения с
использованием PC как регистра. Поэтому CPU вычисляет адрес
операнда по формуле
адрес операнда = (PC) + смещение
Смещение от (PC) в адресных выражениях обусловлено той же
причиной, что и в режиме перехода — необходимостью обеспечить
независимость кода от размещения программы в памяти.
Допускается применение всех трех режимов смещения — А, С
и Е, форматы которых показаны на рис. 8.3. В относительном
режиме правая тетрада байта режима содержит F (или 15) — номер
регистра PC.
Ассемблер, встретив при первом проходе исходную команду с
операндом относительного режима, может вычислить адресное выра-
жение, если только все имена в нем уже находятся в таблице имен.
Он вычисляет смещение, привлекая свой счетчик ячеек, и выбйрает
минимальное число байтов, необходимых для представления смеще-
ния в дополнительном коде. Если какого-либо имени в таблице
имен пока нет, ассемблер рассчитывает на смещение максимально-
го размера — длинное слово, а позднее либо он, либо редактор
153
связей вычисляют и подставляют в команду само смещение. Вот
почему в гл. 4 рекомендовалось размещать директивы резерви-
рования памяти и инициализации в начале программной секции.
Когда метки областей данных будут использоваться в дальнейшем
в программе как операнды, они уже окажутся в таблице имен, и
ассемблер сможет сэкономить память для смещений.
Пример 8.3. Относительный режим. Рассмотрим, как CPU интерпретирует и
выполняет приведенную ниже команду, считая, что она начинается с байта 13А6.
(PC) = 13А6
... 66 FE В9 CF 03 Сб.-
Обработка первых двух байтов тривиальна. Выполняется команда MULL3,
и первым операндом оказывается литерал #3. После обработки получаем:
(РС) = 13А8
... 66 FE В9 CF 03 С5...
Производятся выборка байта режима второго операнда и инкремент (PC)
на единицу:
(PC) = 13А9
... 66 FE В9 CF 03 С5
Байт режима содержит значение CF, поэтому номер режима — С (режим
смещения—слово). Используется регистр F (или 15), т. е. PC.
Осуществляются выборка смещения и инкремент (PC) на два, так как смещение
представлено словом:
(РС) = 13АВ
... 66 FE В9 CF 03 Сб.-
Смещение равно FEB9. Вычисляется адрес второго операнда посредством
суммирования смещения и текущего содержимого PC (13АВ):
(PC): 00001 ЗАВ
+ +
смещение: FFFFFEB9
адрес операнда: 00001264
Вторым операндом в команде MULL3 будет длинное слово, начинающееся
с байта 1264.
Производятся выборка байта режима третьего операнда и инкремент (PC).
Байт режима содержит 66, поэтому для третьего операнда используется регистро-
вый косвенный режим с R6. Выполняется умножение, и результат помещается
в длинное слово, адрес которого находится в регистре R6.
Здесь (PC) = 13АС — адрес следующей команды. Рассмотренная выше команда в
ассемблерной программе может иметь вид:
MULL3 #3, SYMBOL, (R6)
где имя SYMBOL определено в программной секции ранее, а команда появилась
через 142|б байта после него. Отметим, что ассемблеру для смещения SYMBOL
потребовалось слово, так как смещение не помещается в байт.
Приведенный пример позволяет сделать два важных вывода.
Во время вычисления адреса в PC содержится адрес спецификатора
следующего операнда (или следующей команды). Как всегда в
режиме смещения адрес операнда вычисляется в рабочей зоне
CPU и (PC) не изменяется.
Отметим, что для спецификатора операнда в относительном режи-
ме нужно от двух до пяти байт памяти, а во всех регистровых
режимах, кроме режима смещения, необходим всего один байт.
Следовательно, вводя по мере возможности регистровые режимы
154
вместо адресных выражений, программист может писать более
компактные программы.
ФОРТРАН ЯЗЫК АССЕМБЛЕРА МАШИННЫЙ КОД
COST: .BLKF 1
BASEs .BLKF 1
VAR: .BLKF 1
NUMs .BLKL 1
COST-BABE+NUM*VAR
CVTLF NUM,R3 53DBAF4E
MULF2 VAR,R3 53D3AF44
ADDF3 BASE,R3,COST C4AF53CBAF41
Рис. 8.7. Усовёршенствованный пример 1.1
Функции ассемблера. Мы уже рассмотрели ряд функций ассемб-
лера при задании относительного режима, поэтому обратимся к
примеру. Вернемся к самому первому примеру (1.1), который повто-
рен на рис. 8.7. Команды здесь реализуют арифметику с плавающей
точкой, но знать ее необязательно, так как нас интересует механи-
ческая задача трансляции ассемблерных команд в машинный код.
Поскольку машинный код нам известен, будем считать его отправ-
ной точкой и рассмотрим действия ассемблера. Предположим,
что . = 20, когда ассемблер встречает директивы резервирования
памяти. (Можно было бы предположить, что . = 0 или любому дру-
гому значению.) При трансляции машинных команд в его таблице
имен уже находятся следующие элементы:
Имя Значение
COST 20
BASE 24
VAR 28
NUM 2C
Машинный код первой команды:
первый байт
53 DB AF 4Е
Байт 4Е представляет собой код операции команды CVTLF,
a AF — байт режима ее первого операнда. Значит, ассемблер ко-
дирует NUM, используя относительный режим со смещением в
байт. Байт DB и является смещением. Ассемблер «знает», что,
когда при выполнении смещение прибавляется к (PC), программный
счетчик содержит спецификатор следующего операнда, находящийся
155
после смещения. Поэтому основой для вычисления смещения служит
уравнение:
NUM = 2С = адрес следующего байта + смещение
Так как смещение равно DB, значение счетчика ячеек для следую-
щего байта должно быть 2С—DB = 51. Ведя отсчет в обратном поряд-
ке, можно определить, что . = 4Е в начале команды, и выписать
значения счетчика ячеек для каждого байта:
Счетчик ячеек: 51 50 4F 4Е
Машинный код: 53 DB AF 4Е
Обратимся теперь ко второй команде
первый байт
53 D3 AF 44
Байт 44 — это код операции команды MULF2, а байт режима
первого операнда внЬвь равен AF. Ассемблер кодирует имя VAR,
используя относительный режим со смещением в байт. На основе
уравнения
VAR = 28 = адрес следующего байта + смешение
вычисляется значение D3 смещения. Отсюда значение счетчика
ячеек для следующего байта (т. е. ячейки, содержащей специфи-
катор 53 второго операнда) должно быть 28—D3=55. Таким обра-
зом, значения счетчика ячеек для каждого байта второй команды:
Счетчик ячеек 55 54 53 52
Машинный код 53 D3 AF 44
Вторая команда должна следовать сразу за первой и из наших
вычислений видно, что так оно и есть. Читателю рекомендуется
самостоятельно декодировать третью команду и разобраться, как
ассемблер вычисляет смещения для BASE и COST.
В этом примере у нас уже был машинный код, и мы знали, что
ассемблер использовал смещения размером в байт. Рассмотрим
теперь, каким образом ассемблер определит смещение и относитель-
ный режим, если размер смещения заранее не известен. Предполо-
жим, что кодируемый операнд специфицирован именем. При сумми-
ровании (РС) и смещения в регистре РС содержится адрес байта,
следующего за спецификатором операнда; поэтому смещение вы-
числяется с помощью уравнения:
значение имени = адрес байта, находящегося после спецификатора операнда
4-смещение
Проблема заключается в том, что в уравнении два «неизвестных»:
смещение и адрес байта, находящегося после спецификатора операн-
да (последний не известен потому, что он зависит от размера сме-
щения). Предположим, что . =адресу спецификатора операнда с
относительным режимом, с которым мы работаем. Сначала ассемблер
может попытаться определить, не поместится ли смещение в байт,
решая относительно смещения уравнение:
значение имени = . + 2+смещение
156
Адрес байта, находящегося после спецификатора операнда,
будет равен . + 2, так как идентификатор режима и смещение зани-
мают по одному байту. Если смещение находится в диапазоне
— 128 ... 127, оно помещается в байте, и задача решена. Так как
вычисления осуществляются в шестнадцатеричной системе, посмот-
рим, как выглядят допустимые смещения в шестнадцатеричном
представлении,'чтобы для проверки их не нужно было превращать
в десятичные числа. Если в дополнительном коде смещение
можно представить в двух шестнадцатеричных разрядах, оно поме-
щается в байте. Напомним, что левый бит показывает знак числа,
например, при получении отрицательного смещения FF7A его нельзя
запомнить в байте, так как 7 А интерпретируется как положительное
число.
Если найденное в приведенном выше уравнении смещение поме-
щается в слово, а не в байт, задачу нельзя считать решенной и
использовать это смещение. Когда смещение занимает слово,
адрес находящегося после него байта не равен . + 2; он будет равен
.+3, что делает решение недействительным. Однако ясно, что смеще-
ние можно скорректировать, уменьшив его на единицу. Читателю
рекомендуется самостоятельно разобраться в том, как скорректиро-
вать вычисленное смешение, если для него требуется длинное слово.
Непосредственный режим. Непосредственный режим применяется
для ассемблирования литеральных операндов, которые слишком ве-
лики для шестибитового литерального режима адресации. В непо-
средственном режиме (как и в литеральном режиме) команда содер-
жит сам операнд (т. е. данное, используемое командой), а не его
адрес. В большинстве языков Ассемблера термин непосредственный
операнд употребляется именно в этом смысле, хотя в деталях осо-
бенности хранения операнда в разных ЭВМ различны.
Непосредственный режим представляет собой автоинкрементный
режим (режим 8) с использованием РС. Байт режима содержит
значение 8F и непосредственный операнд хранится сразу после
байта режима. Команда
ADDL2 #75, R10 ,
кодируется так:
первый байт
5А 00 00 00 4В 8F СО
R10 #75 ADDL2
Рассмотрим процесс выполнения команды подробнее.
Пример 8.4. Непосредственный режим. Предположим, что команда начинает-
ся с байта 472. Итак, приступим:
(РС)=472
... 5А 00 00 00 4В 8F СО...
Производится выборка кода операции. Командой оказывается ADDL2. Осу-
ществляется выборка байта режима первого операнда. В результате получается:
(РС)=474
... 5А 00 00 00 4В 8F СО...
Байт режима содержит 8F, что соответствует автоинкрементному режиму
с использованием регистра РС.
157
Исходный оператор Машинный код Ражим спецификатора
первого операнда
ADDL2 *75,R10 SA ОО ОО ОО 4В 8F СО непосредственный, длинное слово
ADDW2 •7S,R10 SA ОО 4В 8F АО непосредственный, слово
—
ADDL2 #55,RIO 5А 37 СО литеральный
MOVB #ЛА/3/,(RS) 68 33 90 литеральный
MOVB #лА/1_/, (R8) 68 4С 8F 90 непосредственный, Файт
Рис. 8.8. Литеральный и непосредственный режимы
Адресом операнда является адрес, находящийся в PC, т. е. 474.
Как обычно в автоинкрементном режиме, выполняется инкремент регистра
на число байтов, определяемое типом данных в команде; для длинного слова инкре-
мент равен четырем. Таким образом, (PC) = 4744-4 = 478.
(PC) =478
...5А 00 g0 00 4В 8F СО...
Отметим, что в последнем случае инкремент PC произведен как следствие
автоинкрементного режима, а не как стандартное действие в процессе выполнения
команды.
Производится выборка следующего байта и интерпретация его как байта режи-
ма второго операнда, а затем, как и ранее, инкремент (PC) на единицу; теперь PC
указывает на первый байт следующей команды.
Далее выполняется операция — сложение длинных слов. Адрес первого операнда
равен 474, поэтому данное из длинного слова, начинающегося с этого байта
(0000004В), прибавляется к длинному слову в регистре R10. Длинное слово
0000004В представляет собой дополнительный код десятичного числа 75, являюще-
гося первым операндом в исходном операторе.
Функции ассемблера. При рассмотрении литерального режима
отмечалось, что когда ассемблер встречает операнд, начинающийся
со знака #, он должен выбрать необходимый режим — литеральный
или непосредственный. Предположим, выбран непосредственный
режим. Теперь нужно определить тип данных операнда и правильно
закодировать данное.
На рис. 8.8 показаны несколько исходных операторов и их
машинные коды. Операнд #75 закодирован как длинное слово в
первой команде ADDL2, но как слово во второй команде ADDW2.
Несмотря на то, что третья команда оперирует длинными словами,
операнд #55 закодирован как литерал с использованием одного
байта для всего спецификатора операнда. Напомним, что во время
выполнения CPU расширяет литеральные операнды до требуемого
командой типа данных.
Каждая из двух последних команд на рис. 8.8 пересылает сим-
вольный код в байт памяти. Отметим, что, хотя для символьного
кода обычно используется байт, ассемблер закодировал символ
158
«3» литералом, так как его код 3316 или 51ю помещается в шесть
битов. Однако код символа «L», равный 40ie или 76ю, не помещает-
ся в шесть битов, и поэтому ассемблером выбран непосредственный
режим.
8.6. Листинг ассемблирования
Формат листингов ассемблерных программ был приведен в разд.
6.1. Листинг показывает (при чтении справа налево) исходные
ассемблерные операторы, номер строки, значение счетчика ячеек
в каждой строке, машинный код и данные, которые ассемблер
помешает в объектный файл, а также дополнительную информа-
цию. Здесь мы рассмотрим машинный код подробнее.
На рис. 8.9 приведена основная часть листинга, формируемо-
го ассемблером VAX-11 MACRO. Это несколько модифицирован-
ная и расширенная версия программного сегмента из примера
7.3. Рекомендуется тщательно изучить листинг, так. как в нем
указано большинство режимов адресации, описанных в настоящей
главе.
Разберем подробнее одну из команд (в строке 73):
Машинный код Ячейка Строка
FF29 CF 03 00000035 8F F9 05D1 73
— ——————————— ——
PKD #3 «HIGH КОП
Исходный оператор
CVTLP #НIGH,#3,PKD
В листинге байты машинного кода группируются по своему
назначению, что несколько упрощает интерпретацию кода. Байт
F9 — это код операции команды CVTLP. Первый операнд #HIGH
закодирован в непосредственном режиме, поскольку значение
HIGH=85 слишком велико для литерала. Ассемблер отводит для
HIGH длинное слово, так как первый операнд команды CVTLP
имеет тип длинного слова. Второй операнд #3 достаточно мал
для литерала, поэтому спецификатор операнда равен 03. Третий
операнд дается в относительном режиме. Ассемблер использует
смещение размером в слово. Выяснив, что команда начинается в
ячейке 05D1, и, подсчитав число байтов (или проанализировав ад-
рес следующей команды), можно определить, что адрес ячейки
байта, находящегося после смещения, есть 05DB. Следовательно,
значение PKD должно быть равно: 05DB + FF29 = 0504. Обращаясь
к строке 36, в которой определяется PKD, проверяем правиль-
ность полученного результата.
В программе на рис. 8.9 нет спецификаторов операндов, кото-
рые ассемблер не мог бы обработать. Если в операнде с относитель-
ным режимом фигурирует имя, которое определяется после исполь-
зующей его команды, или если имя вообще не определено в данном
159
0000004В
00000055
00000032
20 68 74 69 77
65 77 74 65 62
20 64
20
20
65
73
6С
65
6Е
61
00
20
0А
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
0000
OQPO
0000
. 00000016 0000
00000018
00000016
ОООООООА
70
72
20
20
00000480
000004В4
00000504
00000506
“ 65 50
63 73
6Е
20
20
6F
6F
20
20
20
65
20
20
1 ; ФОРМУЛИРОВКА ПРОБЛЕМЫ
2;
3; Эта программа просматривает массив, содержащий, фамилии и
4; оценки, и печатает фамилии людей, оценки которых находятся
5; между LOW и HIGH включительно. Данные считываются из
6 ; файла DATA. DAT.
7;
8; ДАННЫЕ
10; SCORES массив фамилий и оценок. Каждый элемент
11; содержит фамилию в символическом коде (NAME_LEN символов)
12; с последующей оценкой (целое число в формате слова)
13 ; ENTRY_LEN число байт в элементе массива SCORES
14; (NAME—LEN+2)
МАХ максимальное число элементов, для которых
Зарезервировайо пространство
COUNT фактическое число элементов (длинное слово)
SCORE смещение оценки от начала элемента
20;
21; Константы
22;
23 LOW = 75
56 FA7C CF
58 32
9Е
DO
0000
0000
0000
0000
0000
0000
0000
0400
04В4
0504
0506
0512
051Е
0532
052А
0530
0530
0580
0580
0580
0580
0580
0580
0580
0580
0580
0580
0585
24 HIGH в85
25 MAX “ 50
26 NAME_LEN = 22
27 ENTRY.LEN = NAME—LEN+2
28 SCORE “NAME-LEN
29 LF “ 10
30;
31; Резервирование памяти
32;
33 SCORES: .BLKB MAX*ENTRY_LEN
34 COUNT: .BLKL 1
35 BUFFER: .BLKB 80
36 PKD: .BLKB 2
37 HDG: .ASCII /Участники с оценками между/
38 LOWSCORE: .ASCII / и/
39HISCORE: ASCIZ / /<LF>
40;
41 BEGIN SCORELIST
42;
43; Эта секция считывает фамилии и оценки из DATA. DAT.
44; Каждая запись содержит фамилию и 3-разрядную оценку.
05Я8
46 ; ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ: R6
47; R8
48; R9
49; R0-R5
50;
51 MOVAB SCORES, R6
52 MOVL #МАХ, R8
53 READ: READRCRD BUFFER
указатель SCORES
счетчик цикла
рабочий
для команды MOVC3
; Указатель SCORES
; Не считывать более МАХ
; Ввести запись
6 Зак. 821
66 FF17 CF 16 28 0598 54 MOVC3 #NAME_LEN, BUFFER, (R6) ; Запомнить фамилию
FF5D CF 03 FF27 CF 03 09 059Е 55 CVTSP #3, BUFFER+NAME-LEN, #3, PKD
59 FF58 CF 03 36 05А7 56 CVTPL #3, PKD, R9
16 А6 59 F7 05AD 57 CVTLW R9, SCORE (R6) Запомнить оценку
56 18 СО 05В1 58 ADDL2 *ENTRY_LEN, R6 ; Инкремент указателя
D1 58 F5 05В4 59 SOBGTR R8. READ ; Управление циклом
FEF3 CF 32 58 СЗ 05В7 60 EOF: SUBL3 R8,*MAX, COUNT Найти число элементов
05BD 61 ;
05BD 62;
05BD 63; Эта секция сканирует массив SCORES и печатает фамилии
05BD 64; Участники с оценками между LOW и HIGH включительно.
05BD 65;
05BD 66; ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ: R6 указатель SCORES
05BD 67; R8 счетчик цикла
05BD 68;
05BD 69; Преобразовать оценки HIGH и LOW для заголовка.
05BD 70;
FF3D CF 03 0000004В 8F F9 05BD 71 CVTLP # LOW, #3, PKD
FF51 CF 03 FF38 CF 03 08 05С7 72 CVTPS #3, PKD, #3, LOWSCORE Нижняя оценка
FF2A CF 03 00000055 • 8F F9 05D0 73 CVTLP ♦ HIGH, *3, PKD
FF47 CF 03 FF25 CF 03 08 05DA 74 CVTPS ♦3, PKD, *3, HI SCO RE Верхняя оценка'
05ЕЗ 75 PRINTCHRS HDG, *41
05F3 76;
58 FEB9 CF DO 05F3 77 MOVL COUNT, R8 Начальный счетчик цикла
27 13 05F8 78 BEQL DONE Массив пустой?
56 FA02 CF 9Е 05FA 79 MOVAB SCORES, R6 ; Указатель SCORES
05FF 80; Сравнить оценку с LOW
004В F8 16 А6 В1 05FF 81 COMPR: CMPW SCORE (R6>, ♦ LOW
14 19 0605 82 BLSS NEXT ; Меньше нижней
0055 8F 16 А6 В1 0607 83 CMPW SCORE (R6) ,* HIGH ; Сравнить оценку с HIGH
ОС 14 060D 84 BGTR , NEXT Выше верхней ,
060F 85 PRINTCHRS (R6),*NAME_LEN Напечатать фамилию
56 18 СО 061В 86 NEXT: ADDL2 ♦ ENTRY—LEN, R6 Инкремент указателя
DE 58 F5 061Е 87 SOBGTR R8, COMPR Управление циклом
0621 88;
0621 89 DONE: EXIT
0650 90 END SCORELIST
Рис. 8.9. Листинг программы
модуле, ассемблер помещает в байт режима EF и оставляет для
смещения длинное слово. Значение смещения подставит редактор
связей. В листинге ассемблер отмечает такие спецификаторы опе-
рандов апострофом, например:
56 00000000 ’EF DO MOVL SYMBOL,R6
8.7. Косвенный
и индексный режимы
Косвенные режимы адресации. Во всех рассматривавшихся до
сих пор режимах адресации операнда в памяти <>го адрес вычислял-
ся с привлечением содержимого регистра (а иногда и смещения).
Однако в некоторых ситуациях в памяти хранится сам адрес. Чтобы
выполнить какую-либо операцию над операндом, можно сначала
переслать адрес из ячейки памяти в регистр, а затем указать
спецификатор операнда регистрового косвенного режима. Здесь
потребуются две отдельные команды. Косвенные режимы адресации
позволяют обращаться к операнду, адрес которого находится в
памяти, всего за один шаг. Спецификатор операнда косвенного
режима имеет вид:
Фспеиификатор.операнда
причем спецификатор операнда может быть в режиме смещения,
автоинкрементном или относительном. Спецификатор операнда об-
рабатывается обычным образом и дает, например, адрес х. Знак
перед спецификатором операнда показывает, что х является не
адресом операнда, а адресом его адреса. Предположим, что длин-
ное слово в памяти, начинающееся по адресу х, содержит у. Тогда
Косвенный операнд: @ спецификатор_операнда
Память Адрес
(длинные
слова)
Спецификатор операнда обрабатывается
как обычно, давая адрес х
Содержимое длинного слова (у), начинающегося
в х, интерпретируется как адрес операнда
Операнд начинается с байта, адрес которого
есть у (это необязательно начало длинного
слова)
Рис. 8.10. Обращение к косвенному операнду
162
у будет служить адресом операнда для команды. Косвенный режим
адресации иллюстрируется рис. 8.10.
Пример 8.5. Косвенный режим со смещением. Рассмотрим рис. 8.11 и команду
MOVW e&(R7)yR10
Предположим, что содержимое регистра R7 составляет 000002А4. При прибавле-
нии смещения 8 получается адрес 2АС. В длинном слове по адресу 2АС находится
код 000000F2. Следовательно, адрес первого операнда для команды MOVW равен
000000F2. Слово, начинающееся по этому адресу, копируется в регистр R10.
Команда : MOVW @8(R7), R10 ~
Память *
(длинные
слова)
Адрес
R7
|000002А4 |
Спецификатор операнда с режимом
смещения определяет адрес
2А4 + 8 = 2АС
Операндом является слово, начина-
ющееся в 0F2
Результат в регистре R10: |xxxxFF3A]
хххх — неизменяемые биты
Рис. 8.11. Команда, в которой применяется косвенный режим со смещением
।
Пример 8.6. Относительный косвенный режим. Предположим, что LOC — метка
длинного слова. Спецификатор операнда
GLOC
показывает относительный косвенный режим: длинное слово в LOC содержит адрес
операнда. После просмотра таблицы и запоминания найденного в LOC адреса
элемента можно применить этот спецификатор операнда для выполнения некоторой
операции над элементом.
Пример 8.7. Автоинкрементный косвенный режим. Спецификатор операнда
G(R8)+
иллюстрирует автоинкрементный косвенный режим. Адрес в регистре R8 есть адрес
адреса операнда. Инкремент регистра R8 производится на четыре независимо от
типа данных команды, так как R8 адресует адрес, который всегда представлен
длинным словом.
6* 163
Пример 8.8. Применение косвенной адресации в программе. Предположим,
имеется набор записей о студентах, каждая из которых включает фамилию сту-
дента, его адрес, курс и другую информацию. Требуется напечатать список сту-
дентов в алфавитном порядке.*
Поскольку записи довольно большие, упорядочивать их целиком малоэффектив-
но. Даже если бы это обстоятельство не препятствовало решению, переупорядо-
чение записей может оказаться недопустимым по каким-то иным причинам, например,
может потребоваться, чтобы они хранились в порядке следования идентификацион-
ных номеров (1D) студентов. Для решения этой задачи можно образовать массив,
содержащий адреса записей обо всех студентах. Такие адреса называются указа-
телями. Первоначально указатели находятся в том же порядке, что и сами записи.
Согласно алгоритму сортировки указатели перестраиваются по результатам анализа
и сравнения фамилий студентов. В итоге порядок размещения указателей будет
соответствовать алфавитному порядку фамилий (рис. 8.12).
Записи
Указатели (до сортировки)
NAME
(22 символа)
Другие
данные
NAME
(22 символа)
Другие
данные
Адрес первой записи
Адрес второй записи
Адрес последней записи
NAME
(22 символа)
Другие
данные
Указатели (после сортировки)
Адрес записи первой
по алфавиту
Адрес записи второй
по алфавиту
Адрес записи последней
по алфавиту
Рис. 8.12. Сортировка с помощью массива указателей
Предположим, что регистр R7 содержит адрес элемента в массиве указателей
(рис. 8.13), и в процедуре сортировки необходимо сравнивать фамилии^ адресуемые
двумя соседними указателями. Такое действие осуществляется по команде
СМРСЗ #22, @0 (R7), @4 (R7)
Как будет показано в следующей главе, одно из основных при-
менений косвенного режима со смещением связано с доступом к
аргументам процедур.
Машинный код косвенных режимов адресации. Каждый косвен-
ный режим адресации имеет свой номер режима. Напомним, что
фактически существуют три режима смещения, различающиеся раз-
мерами смещения (байт, слово, длинное слово). Напомним также,
что относительный режим является просто частным случаем режима
164
Указатели
СМРСЗ #22, @0(R7), @4(R7)
смещения с использованием регистра РС. Номера режимов и форма-
ты машинного кода приведены в табл. 8.1 (см. разд. 8.9).
Пример 8.9. Машинный код относительного косвенного операнда. Предполо-
жим, что имя ADDR имеет значение 22|б и что с байта 181 начинается следующая
команда:
Спецификатор второго операнда кодируется в виде:
FE9C DF
Здесь FE9C — смещение (слово), a DF — байт режима (D — относительный
косвенный режим со смещением размером в слово).
Вся команда принимает вид:
186 181
69 FE 9С DF 1С 28
(R9) QADDR «28 M0VC3
При выполнении команды смещение FE9C прибавляется к 186 и образуется
адрес второго операнда: 0186 + FE9C = 0022 = ADDR. Ассемблер определяет смещение ,
так же, как и в относительном режиме.
165
Индексные режимы. Напомним, что адрес z-го элемента массива
выражается следующей общей формулой:
адрес «-го элемента = адрес_массива-Ь (t—1) «размер_элемента = адрес_
массива — размер__элемента +<» размер_элемента
В этой формуле предполагается, что индексы массива начинают-
ся с единицы (см. рис. 6.2). Если индексы массива начинаются с
нуля, как это принято в некоторых языках высокого уровня, фор-
мула упрощается:
адрес i-го элемента = адрес _ массива +<*размер _элемента
В любом случае для вычисления адреса элемента приходится
выполнять арифметические операции. Поскольку обращения к мас-
сивам встречаются в языках высокого уровня довольно часто, в
системе VAX предусмотрены режимы адресации, рассчитанные на
вычисление адресов элементов массива. Такие режимы называются
индексными. Адрес операнда складывается из двух частей — базо-
вого адреса и индекса. Базовый адрес задается с помощью любого
из рассмотренных выше режимов, а индекс находится в регистре.
Спецификатор операнда имеет следующий общий вид:
спецификатор _ базы [Rx]
где Rx— один из регистров RO,....,R11, АР, FP или SP. Процессор
определяет тип данных операнда, т. е. его размер в байтах, по коду
операции команды и вычисляет адрес операнда по формуле
адрес операнда = базовый адрес + (Rx) «размер
Здесь размер — это число байтов в операнде. Таким образом,
в индексном регистре Rx должен храниться индекс выбираемого
элемента массива, а базовый адрес является адресом первого бай-
та массива (или адресом массива минус размер, если индексирование
начинается с единицы). Так как в вычислениях используется все со-
держимое Rx, индекс представляет собой длинное слово.
Любой режим адресации может быть индексным. Другими слова-
ми, для задания базового адреса допустим любой режим, кроме ли-
терального, непосредственного, регистрового и индексного, а также
режима перехода.
Пример 8.10. Относительный индексный режим. Пусть LIST — массив слов,
элементы которого индексируются, начиная с нуля, содержимое регистра R8 состав-
ляет 00000005. В команде
TSTW L1ST[R8]
спецификатор операнда L1STJR8] адресует пятый элемент массива. Вычислен-
ный адрес операнда равен L(ST+5*2.
Если элементы в LIST индексируются, начиная с единицы, пятый элемент
адресуется как LIST-I-2 [R8].
Пример 8.11. Реализация оператора ''вычисляемый GOTO" языка Фортран.
Оператор "вычисляемый GOTO" представляет собой команду перехода по несколь-
166
ким направлениям. Он имеет следующий формат:
GOTO (si, $2,..«
где / — это переменная целочисленного типа, a si, S2,.—, «л — метки операторов.
Если значение переменной / находится между 1 и Л, оператор GOTO
вызывает переход к оператору, отмеченному s/. Его можно реализовать
на языке Ассемблера последовательностью команд сравнений и условных
переходов, которые сравнивают i с 1, затем с 2 и т. д., но такой прием оказывается
неэффективным. Компилятор может сформировать в памяти таблицу, содержащую
назначения переходов, и использовать для выбора нужного назначения индексный
режим. Предположим, что таблица имеет название GOTO_____TABLE, а теку-
щее значение i находится в регистре R5. Оператор "вычисляемый GOTO" реализуется
следующим образом:
MOVL I,RS 1 Ваять 1
BLEQ BAD.INDEX 1 Перейти, если 1 < 0
CMPL R5,K 1 Проверить большое 1
BGTR BAD.INDEX 1 Перейти, если 1 > К
MOVL 0OTO.TABLE-4CR53,R6 1 Ваять нааначение
□MP (R6) 1 Перейти по нааначению
Машинный код индексных режимов адресации. Индексный специ-
фикатор операнда кодируется байтом режима для индексного режима
(режим 4), за которым следует полный спецификатор операнда
для базового адреса. Поэтому
спецификатор_базы [Rx]
кодируется следующим образом:
первый байт (байт режима)
спецификатор базового адреса 4х
Пример 8.12 Некоторые индексные спецификаторы операндов. В машинном
коде как обычно первый байт спецификатора операнда показан справа.
формат яаыка Ассемблера Машинный код
(R8)CR53 68 45
LI8TCR73 смещение CF 47
Q4(R7)CR1O3 04 В7 4А
8.8. Ошибки времени выполнения
Особые случаи. Выше было показано, каким образом CPU
использует программный счетчик для выполнения команд, включая
и команды переходов, в определенной последовательности. Этот
процесс может завершиться «без происшествий», но может быть
167
и прерван ошибкой или необычным условием. Мы не будем касаться
вопроса о том, корректно ли реализуются вычисления программой и
получает ли она правильный результат, а лишь рассмотрим действия
CPU, который «ничего не знает» о намерениях программиста.
Он просто чисто механически дешифрует команды и выполняет опе-
рации над данными. В этом разделе описаны некоторые ошибочные
ситуации, когда нормальный ход выполнения программы прерывается
до ее завершения. Такие ситуации называются особыми случаями.
Что же может нарушить процесс выполнения программы? Для
этого имеется несколько потенциальных причин. Байт, который
CPU интерпретирует как код операции, может быть недопустимым
кодом операции или соответствовать привилегированной команде (не-
доступной обычному пользователю). Используемый для операнда
режим адресации или сам операнд может оказаться неподходящим
для команды. Что-то может произойти с результатом операции,
например переполнение.
У начинающего программиста подобные события почти всегда
связаны с ошибками в программе и обычно ведут к прекращению ее
выполнения (с выдачей сообщения об ошибке).
Изложенное выше не является чем-то специфическим для си-
стемы VAX. Особый случай — это общепринятый термин, обозна-
чающий такую ситуацию в команде, в которой прерывается нормаль-
ный ход выполнения программы. Упомянутые ранее особые случаи,
например недействительные коды операций или операнды, а также
неправильные результаты, имеют место и в других ЭВМ. Конечно,
точные условия возникновения особых случаев и реакции на них
варьируются в различных вычислительных системах.
Может оказаться, что программист не хочет прекратить выпол-
нение программы из-за особого случая. Многие системы допускают
определенную гибкость при решении этого вопроса в конкретных
условиях. Для некоторых особых случаев в системе VAX можно
задать вызов специальной процедуры (подпрограммы обработки
условия), чтобы проанализировать причины возникновения особого
случая и продолжить выполнение программы. Применение специаль-
ных процедур описано в руководстве VAX-11 Architecture Handbook
и в настоящей книге не рассматривается. Для нас особые случаи
представляют интерес лишь с той точки зрения, что они являются
следствием ошибок в программе и прекращают ее выполнение.
В системе VAX особые случаи делятся на три класса. Особые
случаи, которые возникают при выполнении команды и препятствуют
ее завершению, называются ошибками. Особые случаи, возникающие
в конце выполнения команды, относятся к классу специальных
прерываний. Команда выполняется успешно, но результат получается
неправильным. (Программист может разрешать или запрещать неко-
торые специальные прерывания, т. е. указывать, должны или не
должны возникать особые случаи. Если прерывание запрещено,
обычным образом выполняется очередная команда. Вопрос о том, как
168
разрешать и запрещать прерывания, мы рассмотрим после обсужде-
ния особых случаев.) При наличии ошибок и специальных преры-
ваний состояние программы сохраняется — содержимое памяти, ре-
гистров и PSL оказывается доступным. Следовательно, можно выя-
вить местоположение «подозрительной» команды, система может
выдать полезную информацию в сообщении об ошибке и, если про-
граммист определил подпрограмму обработки условия, выполнение
программы можно продолжить. Особый случай, называемой ан-
нулированием, обусловливается более серьезными причинами и при-
водит к неопределенному состоянию регистров и ячеек памяти.
Как правило, выяснить причину такого особого случая или возобно-
вить выполнение программы невозможно.
Ниже мы перечислим особые случаи с указанием их названий
(введенных фирмой DEC), дадим перечень возможных ошибок
и объясним их смысл, а также отметим причины возникновения
таких ошибок.
ЗАРЕЗЕРВИРОВАННЫЙ КОД ОПЕРАЦИИ
Код операции определяет недействительную или привилегиро-
ванную команду.
Возможные причины — программа «перешла» в область данных
или запомнила данные среди команд.
НАРУШЕНИЕ УПРАВЛЕНИЯ ДОСТУПОМ
Программа пытается обратиться к памяти вне разрешенной для
нее области.
Возможные причины — неправильно вычислен адрес; цикл обра-
ботки массива слишком продолжителен, в литерале пропущен
знак #.
ЗАРЕЗЕРВИРОВАННЫЙ РЕЖИМ АДРЕСАЦИИ
Использован неправильный режим адресации, например, в ка-
честве операнда-получателя указан литерал.
Возможные причины — нарушен порядок записи операндов, про-
граммист неправильно понимает команду.
ЗАРЕЗЕРВИРОВАННЫЙ ОПЕРАНД
Выбираемый операнд не имеет корректного типа данных для
команды. Любую цепочку битов можно интерпретировать как целое
число в дополнительном коде, но в некоторых типах данных (числа
в символьном коде, числа с плавающей точкой, упакованные деся-
тичные числа) часть двоичных наборов оказывается недействитель-
ной. Этот особый случай может трактоваться как ошибка или
аннулирование в зависимости от того, где он возникает.
Возможные причины — неопытные программисты часто допуска-
ют подобные ошибки при использовании команды CVTSP для пре-
образования входных данных, представленных в символьном коде.
169
если неправильно сформатированы сами данные или неверно вы-
числен адрес.
ПРЕРЫВАНИЕ ПО ЦЕЛОЧИСЛЕННОМУ ПЕРЕПОЛНЕНИЮ
Для результата выполнения целочисленной арифметической опе-
рации требуется больше места, чем имеет операнд-получатель. (В
операнде-получателе запоминаются младшие разряды результата.)
Возможные причины — неправильно выбран тип данных; в коман-
де использованы ошибочные данные, так как адрес операнда вычис-
лен некорректно.
ПРЕРЫВАНИЕ ПО ЦЕЛОЧИСЛЕННОМУ ДЕЛЕНИЮ НА НУЛЬ
В команде целочисленного деления указан нулевой делитель.
Возможные причины — использованы ошибочные данные из-за
неверно вычисленного адреса или программист забыл учесть по-
тенциально нулевой делитель.
ОСОБЫЕ СЛУЧАИ АНТИПЕРЕПОЛНЕНИЯ, ПЕРЕПОЛНЕНИЯ И ДЕЛЕНИЯ
НА НУЛЬ ЧИСЕЛ С ПЛАВАЮЩЕЙ ТОЧКОЙ
Переполнение и антипереполнение (исчезновение порядка)
возникают в тех случаях, когда порядок результата арифметической
команды выходит за пределы допустимого для чисел с плавающей
точкой диапазона.
Возможные причины — те же, что при возникновении соответ-
ствующих особых случаев целочисленной арифметики. Конечно, воз-
можно, что данные просто находятся вне диапазона, допустимого
для команд системы VAX.
ПРЕРЫВАНИЯ ПО ПЕРЕПОЛНЕНИЮ И ДЕЛЕНИЮ НА НУЛЬ ДЕСЯ-
ТИЧНЫХ ЧИСЕЛ
Возникают при использовании команд для выполнения опера-
ций над упакованными десятичными числами.
Разрешение и запрещение прерываний. Слово состояния прог-
раммы PSW содержит несколько доступных программисту флагов
разрешения прерываний: переполнение в операциях с десятичными
операндами (DV), антипереполнение в операциях над числами с
плавающей точкой (FU) и переполнение в операциях с целыми
числами (IV). Флаги размещены в PSW следующим образом:
15 876543210
ООО 0.0 0 0 0DVFLIIV, Т. N Z V С.
Флаги разрешения Коды условий
прерываний
При наличии одного из указанных условий особый случай
возникает тогда и только тогда, если соответствующий флаг раз-
решения прерывания установлен в состояние 1. Первоначально
все флаги находятся в состоянии 0 (прерывания запрещены).
Прерывания IV и DV можно разрешать или запрещать в начале
170
а) Сообщение об ошибке
%SYSTEM-F-ACCVIO, нарушение доступа, маска причины=05, виртуальный адрес-31000400, РС=00000ВВЗ,
%TRACE—F—TRACE BACK, ниже дается символический дамп PSL=0BC00024
имя модуля имя программы строка относительный PC абсолютный PC
.MAIN . BLANK 00000598 00000BB3
б) Часть листинга неправильной программы
0580 43 ; Эта секция считывает фамилии и оценки из DATA. DAT.
0580 44 ; Каждая запись содержит фамилию и 3-разрядную оценку.
66 56 FF17 FA7C 58 CF CF 32 16 90 D0 28 0580 0580 0580 0580 0580 0580 0581 0585 0588 0598 45 46 47 48 49 50 51 52 53 54 ; ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ: R6 ; R8 указатель SCORES счетчик цикла рабочий для команды MOVC3 ,* Указатель SCORES' ; Не считывать более МАХ ,* Ввести запись ; Фамилия
9 9 MOVB MOVL READ: READRCRD MOVC3 R9 R0-R5 SCORES, R6 # MAX, R8 BUFFER # NAME.LEN, BUFFER (R6)
FF5D CF 03 FF27 CF 03 09 059Е 55 CVTSP # 3, BUFFER+NAME_LEN, #3, PKD
59 FF58 CF 03 36 05А7 56 CVTPL # 3, PKD, R9 ; Преобразовать
16 А6 59 F7 05AD 57 CVTLW R9, SCORE (R6) ; и запомнить оценку
56 18 СО 05 05В1 58 ADDL2 # ENTRY—LEN, R6 ; Инкремент указателя
D1 58 F5 05В4 59 SOBGTR R8, READ ; Управление циклом
FFF3 CF 32 58 СЗ 05В7 60 EOF: SUBL3 R8,#MAX, COUNT ; Найти число элементов
Рис. 8.14. Ошибка времени выполнения
выполнения основной программы или процедуры, а любой флаг мож-
но устанавливать или сбрасывать машинными командами B1SPSW
(установить бит в PSW) и B1CPSW (сбросить бит в PSW) в произ-
вольной точке программы. Макрокоманда BEGIN разрешает оба
вида прерываний. Управление флагами IV и DV рассматривается
в следующей главе. Таким образом, если, например, в процедуре
потенциально возможно целочисленное переполнение, но его обраба-
тывает сама программа, программист может запретить прерывание
IV при выполнении этой поцедуры, предусмотрев команды проверки
переполнения и его обработки без возникновения особого случая.
Бит Т (бит 4) в PSW представляет собой флаг прерывания
покомандной работы: он используется символическим отладчиком
системы VAX-11.
Интерпретация сообщений во время выполнения. Сообщением об
ошибке программист уведомляется о виде возникшей ошибки
и местоположении вызвавшей ее команды. Местоположение опи-
сывается как «относительный РС», т. е. фактическое содержимое
PC минус начальный адрес программной секции. Это значение
соответствует значениям счетчика ячеек, показанным в листинге
программы, что помогает программисту найти ошибку.
Пример 8.13. Использование сообщения об ошибке для отладки программы.
Предположим, что выполнялась программа, аналогичная приведенной на рис. 8.9.
На рис. 8.14,а показано' выведенное сообщение об ошибке и отмечены его наибо-
лее важные фрагменты.
Ошибка, заключающаяся в нарушении доступа, возникла в относительной ячейке
с адресом 00000599. На рис. 8.14,6 представлена часть листинга. Как'мы видим,
ошибка возникла в команде MOVC3, находящейся в строке 54. Очевидно, адрес
в регистре R6 —неправильный. Вернувшись к строке 51, где инициализировался
регистр R6, мы обнаружим в ней команду MOVB, вместо которой должна быть
команда MOV АВ.
8.9. Заключение
Центральный процессор использует программный счетчик РС как
указатель следующего обрабатываемого байта в командном потоке.
Он производит автоматический инкремент РС по мере выборки
каждого фрагмента команды.
Команды представляются в машинном коде одно- или двухбайт-
ным кодом операции и последующими спецификаторами операндов
для каждого операнда. Способы спецификации операндов называют-
ся режимами адресации. В системе VAX предусмотрено множество
разнообразных режимов адресации. Каждый спецификатор операн-
да (за исключением операндов в режиме перехода) начинается
с байта режима, включающего номер режима адресации и номер
регистра (или литерал). По номеру режима CPU может определить,
сколько дополнительных байтов (если они есть) содержит специ-
фикатор операнда.
Большинство режимов адресации требует применения общего
172
регистра. Программный счетчик РС используется не для всех
режимов. Те же режимы адресации, для которых РС необходим,
носят специальные названия и требуют особого рассмотрения,
так как привлечение РС может иметь нежелательные последствия.
В режиме перехода и в относительных режимах адрес операнда
вычисляется посредством суммирования смещения и (РС). Следо-
вательно, для кодирования адресных выражений достаточно указать
их положения относительно (РС). Такой прием обеспечивает пра-
вильное кодирование адресного выражения независимо от того, в ка-
кую область памяти загружается программа.
Вычисления адресов всегда производятся с 32 битами, поэтому
смещения при необходимости расширяются со знаком.
Режимы смещения особенно удобны для адресации полей в струк-
туре данных, представленной в стандартном формате. Косвенные
режимы обеспечивают программисту доступ к .операнду, адрес ко-
торого хранится в памяти, без первоначальной загрузки адреса
в регистр. Индексные режимы предназначены для адресации элемен-
тов массивов.
Режимы адресации сведены в табл. 8.1.
Ассемблер транслирует исходные программы с языка Ассемблера
в машинный код. Большинство ассемблеров осуществляет трансля-
цию в два этапа (за два прохода по исходной программе); они на-
зываются двухпроходовыми ассемблерами.
Основная цель первого прохода — найти все имеющиеся в ассем-
блируемом модуле имена, определить их значения (если это воз-
можно) и сформировать по ним таблицу имен. Для определения
значений имен в ассемблере предусмотрен счетчик ячеек, который
инициализируется на нуль. Производится инкремент счетчика ячеек
на число байтов, используемых для каждого оператора (машинных
команд и директив резервирования памяти для данных). Встретив
имя в поле метки команды, ассемблер присваивает этому имени
текущее значение счетчика ячеек.
При втором проходе ассемблерные команды преобразуются в ма-
шинный код. Некоторые операнды можно кодировать с помощью
режимов адресации. В ассемблер встроен набор правил, определя-
ющих выбор им режима адресации. Часть из них приведена в
настоящей книге, а с остальными можно познакомиться в руко-
водстве VAX-11 MACRO Language Reference Manual. Существует
ряд адресных ссылок, которые ассемблер не может обработать; он
оставляет их для заполнения редактору связей.
Особый случай — это ситуация, возникающая во время (или
после) выполнения команды, когда требуется вмешательство опе-
рационной системы. Особые случаи вызываются «плохими» кодом
операции, режимом адресации, адресом или операндом, а также
анормальным свойством результата, например переполнением. Как
правило (особенно у неопытных программистов), особый случай
173
возникает из-за наличия ошибки в программе. Выводимые системой
сообщения об ошибках уведомляют программиста о типе возможной
ошибки и местоположении вызвавшей ее команды.
Таблица 8.1
Режимы адресации ,
Название Номер Формат ассемблера Машинный код
Регистровые режимы Регистровый Регистровый косвенный Автодекрементный Автоинкрементный Со смещением (байт) Со смещением (слово) Со смещением (длинное слово) 5 6 7 8 А С Е Rn (Rn) — (Rn) (Rn) + /CMeui(Rn) tfcMeui(Rn) CMeui(Rn) VfcMeuilRn) ACMeu^(Rn) L*CMeiy(Rn) 1 5л 6/г 1п Ъп смещ Кп смещ Сп смещ Ей
Литеральный режим 0—3 ^выражение S ^выражение 00 литерал
Режим перехода нет адресное выраже- ние смещ смещ
Режимы программного счетчика Непосредственный 8 ^выражение Г ^выражение данное 8F
Относительный (байт) Относительный (слово) Относительный (длинное слово) А С Е адресное выраже- ние адресное выражение адресное выражение смещ AF смещ CF смещ EF
Косвенные режимы адресации Автоинкрементный косвенный Косвенный со смещением (байт) Косвенный со смещением (сло- во) Косвенный со смещением (длин- ное слово) Относительный косвенный (байт) Относительный косвенный (слово) Относительный косвенный (длинное слово) 9 В D F В D F @(R/i) + @ смещен) @ CMeiy(Rn) @ смещ (Rn) @ адресное выра- жение @ адресное выра- жение @ адресное выра- жение 9/г смещ В/г смещ Dn смещ Fn смещ BF смещ DF смещ FF
Индексный режим1 4 спецификатор базы [Rx] . . . тп 4х
1 Здесь ш — номер режима, а п — регистр, используемый как спецификатор базы операнда. Может быть любой режим, кроме регистрового, литерального, непосредственного и индексного, а также режима перехода.
174
8.10 Упражнения
1. Преобразуйте каждую из команд в машинный код: a) ADDL3 R8, R9, R4,
б) MOVC3#4,(R7), (R8), в) INCW (R5), г) MOVB (R10) +, -(SP).
2. Дешифруйте следующие команды и запишите их в виде исходных ассемблер-
ных операторов, считая, что байт с меньшим адресом находится справа: а) 59 67
С6 б) 6А 68 0А 28, в) 84 D5.
3. Каждая из приведенных ниже команд устанавливает все биты регистра
R6 в состояние 0, но они могут различаться по длине и времени выполнения.
Определите длину команд: a) MOVL#0,R6, б) SUBL2 R6.R6, в) CLRL R6,
г) MULL2#0,R6.
4. Дайте пример команды SOBGTR, которая занимает в машинном коде более
3 байт.
5. Предположим, что медицинская карточка пациента хранится как запись, состоя-
щая из нескольких полей, которые перечислены в их порядке следования:
номер медицинской карточки
фамилия
адрес
дата рождения
группа крови
аллергия
дата последнего обследования
другие поля
8 байт
20 байт
48 байт
6 байт
1 байт
2 байта
6 байт
95 байт
а) Пользуясь режимом смещения, напишите спецификаторы операндов (в ас-
семблерной форме) для первых семи полей, если регистр R7 содержит адрес записи.
б) Напишите ассемблерный оператор для загрузки адреса следующей записи
в регистр R7, если сразу за данной записью в памяти находится запись другого
пациента в точно таком же формате.
в. Напишите последовательность команд для задачи из примера 7.5, пользуясь .
режимом смещения.
7. Предположим, что в памяти хранится набор записей, но они не размещены
в смежном блоке. Третье длинное слово в каждой записи содержит адрес логи-
чески следующей записи. Напишите команду для загрузки в регистр R7 адреса
следующей записи, если R7 содержит адрес некоторой записи.
8. Для каждого участника викторины имеется блок информации в следую-
щем формате:
первый байт
код идентификации возраст QI Q2 Q3 Q4 Q5 Q6 Q7
Здесь код идентификации представлен четырьмя символами, возраст — цело-
численным байтом, a QI,./.., Q7 являются ответами на вопросы и представлены
числами от 1 до 5 в коде ASCH. Считая, что регистр R8 содержит адрес одного
из таких блоков, напишите команды инкремента счетчика в регистре R10, если
возраст участника викторины находится в диапазоне 20—29 лет, а в ответ на
пятый вопрос он проставляет 3 или более. Затем независимо от операции счетчи-
ка (произведен инкремент или нет) в регистр R8 необходимо загрузить адрес
блока информации для следующего участника (считая, что в памяти блоки явля-
ются соседними).
9. Закодируйте команду
BRW NEXT
считая, что она начинается по адресу 48Е и NEXT = 4D5.
10. Постройте машинный код команды
AOBLSS#10, R3, LOOP
если она начинается в 2D1 и LOOP = 2A9.
11. Декодируйте приведенные ниже машинные коды команд, начинающихся
с байта 25Е, считая, что байт с младшим адресом находится справа: При исполь-
зовании относительного режима найдите адрес операнда.
175
а) Адреса:... 265 264 263 262 261 260 25F 25E 25D ...
Память ...AC 58 6А FF CF CF 15 29 87...
б) Адреса:... 262 261 260 25F 25E 25D 25C ...
Память... 57 D5 F2 59 F5 56 20...
12. Предположим, что, когда встречается команда
SUBL3 ALPHA,R9.R10
содержимое счетчика ячеек ассемблера составляет 14D. Покажите кодирование
спецификатора первого операнда и адреса его байтов для каждого из следующих
значений ALPHA: a) ALPHA = 0В8, б) ALPHA = 03С, в) ALPHA = 0D0, г) ALPHA =
= 0CF.
13. Найдите значения BASE, VAR и NUM и определите ячейки, с которых
начинается каждая из трех команд, если для программного сегмента на рис. 8.7
таблица имен содержит значение 38 имени COST.
14. Найдите машинный код команды
ADDL2 ALPHA,ALPHA
считая, что ALPHA = ЗС и команда начинается с адреса 12А. Будут ли специфи-
каторы обоих операндов идентичны? Если да (или нет), то по какой причине?
15. Пусть в программе имеются две идентичные команды:
At)DL2 BETA.R7
ADDL2 BETA.R7
Будут ли идентичными их представления в машинном коде? Если да (или нет),
то по какой причине?
16. Предположим, что вы пишите секцию ассемблера для кодирования операн-
дов относительного режима. Значение кодируемого имени находится в регистре R6,
а значение счетчика ячеек — в регистре R10. Напишите команды для вычисления
смещения, запоминания его в D1SPL и инкремента R10 таким образом, чтобы
этот регистр содержал адрес байта, находящегося после спецификатора операнда.
Сопроводите команды комментариями.
17. Любые имена, фигурирующие в коэффициенте повторения директив .BYTE,
WORD и LONG, должны быть определены до использования директив. Почему?
18. Допустим, ВЕТА = 2А. Мы имеем команду 1
1NCB ВЕТА
а) В какой ячейке должна появиться указанная команда, чтобы смещение
в спецификаторе операнда было равно 80 (т. е. — 128ю)? Это наибольшее отри-
цательное число, представимое в байте.
б) В какой ячейке должна появиться команда, чтобы смещение было равно
ЕЕ7Е(или - 130ю)?
в) Существует ли ячейка, в которой может появиться команда, чтобы смещение
для ВЕТА было равно FF7F ( — 129ю)? Если да, то что это за ячейка? Если нет,
объясните причину.
19. В некоторых ЭВМ нет специального режима адресации для назначений
переходов. Адреса переходов кодируются точно так же, как и другие адресные
выражения. Назовите одно достоинство и один недостаток применения в системе VAX
для адресов переходов режима перехода вместо относительного режима.
20. Какие из следующих команд вызовут ошибки во время ассемблирования?
(Укажите неправильную часть команды.)
a) MNEGB ВЕТА,—5(R7), где ВЕТА=1Е и команда начинается по адресу
18А; б) ADDL2 R7, #28; в) AOBLSS R9.R4.LOOP, где LOOP=OOE2 и команда
начинается по адресу 1СЗ; г) СМРВ (R3), #100.
21. Образуйте машинный код для каждой из следующих команд:
a) SUBW2#30,R8, б) SUBW2#70,R8.
22. Найдите машинные коды для всех перечисленных ниже команд, считая
в каждом случае, что LOOP=OOE2 и команда начинается с адреса 14Е:
а) АССВ#10, 2.R8.LOOP, б) АСВВ#80,#2, R8, LOOP, в) AOBLSS#10,R8,LOOP,
г) AOBLSS#80,R8,LOOP.
23. Декодируйте команду, начинающуюся по адресу 6Е7. Если для какого-
либо из операндов используется относительный режим, введите имя и найдите
адрес, к которому оно относится.
176
Адреса: 6Е9 6Е8 6Е7
Память: 6А FD 86 CF 01 2С 8F 29
24. В рассмотренных выше примерах максимальная длина спецификатора опе-
ранда составляла 5 байт. Напишите команду, в которой спецификатор операнда
состоит из 9 байт.
25. Перечислите все режимы адресации операндов, которые имеются в программе,
показанной на рис. 8.9.
26. С помощью приведенного на рис. 8.9 листинга определите, сколько байтов
занимают макрокоманды READRCRD и PRINTCHRS.
27. Найдите машинный код для следующих спецификаторов операндов:
a) Q12CRS) б) В12(АР)
28. Рассмотрите задачу из примера 8.8, в котором набор записей сортируется
без физической перестройки. Пользуясь алгоритмом пузырьковой сортировки из
упражнения 31 гл. 7, напишите программный сегмент для перестройки массива
указателей, чтобы учесть упорядочение записей.
29. Предположим, что в каждой записи из примера 8.8 имеется поле UNITS,
в котором указано число частей курса, пройденных студентом. Поле UNITS пред-
ставляет собой целое число в формате слова, занимающее 31-й и 32-й байты
записи. Допустим, регистр R7 содержит адрес одного из указателей в массиве
указателей (например, как показано на рис. 8.13). Напишите команду (или коман-
ды) для проверки того, что студент, чья запись адресуется указателем, изучил
96 и более частей курса. (Можно ли использовать для этого косвенный режим
адресации?) ,
30. Напишите программную секцию, в которой для решения задачи из при-
мера 6.9 применяется индексный режим.
31. Напишите программную секцию для реализации пузырьковой сортировки
(см. упражнение 31 гл. 7) с применением индексного режима.
32. Обратитесь к рис. 7.6, где представлена программа двоичного поиска.
Приведенные ниже команды вычисляют а затем сравнивают ключи. адрес анализируемого элемента таблицы,
I ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ! R5 размер элемента i число байт)
1 R6 размер таблицы
1 R7 ключ (слово)
1 R1O середина (длинное слово)
R11 адрес проверяемого элемента
MULL3 R1O,R5,R11
I Середина « размер
ADDL2 R6.R11
I Адрес элемента
CMPW R7,(R11)
1 Сравнить ключи
Можно ли заменить три машинных команды следующей командой, в которой
применяется индексный режим:
CMPW R7,(R6) [R10] ; Сравнить ключ со средним элементом
Ответ обоснуйте.
33. Предположим, что программа реализует арифметические операции над тет-
расловами (с помощью команд ADDL2 и ADWC). В каком состоянии должен
находиться флаг IV? Укажите причину.
177
Глава 9
Процедуры
9.1. Преимущества
использования процедур
и проблемы их реализации
Преимущества использования процедур. Процедурой, или под-
программой, называется программная секция, которая выполняет
конкретную задачу и может быть «вызвана» (использована) дру-
гими процедурами или программами. Процедура обрабатывает пе-
реданные ей вызывающей программой данные, а по завершении
своей задачи возвращает управление этой программе. Процедуру
можно ассемблировать (или компилировать в случае языков высоко-
го уровня), разрабатывать и отлаживать независимо от тех про-
грамм, которые в конце концов будут использовать ее.
Применение процедур обусловлено несколькими причинами.
Основная из них заключается в том, что благодаря процедурам
упрощается разработка, кодирование, отладка и сопровождение
больших программ. Конечно, это не связано с какими-то таинствен-
ными свойствами процедур; они просто дают возможность про-
граммисту решать сложные задачи давно известным методом —
разделить задачу на несколько подзадач и рассмотреть каждую из
них в отдельности.
Целесообразно кодировать любую логически автономную задачу
в виде отдельной процедуры. Дать точное определение «логически
автономной задачи» нелегко, так что начинающему программисту
придется экспериментальным путем находить решение и делить
большую программу на процедуры. Программа, состоящая из не-
скольких процедур, называется модульной.
Реальные программы часто бывают довольно большими (в тыся-
чи строк) и требуют длительной разработки, поэтому независи-
мость процедуры от остальной части программы очень важна при
написании последней. Программисту объясняется, какую задачу
должна решать процедура, какие обрабатывать данные и какой
178
результат (если он есть) возвратить вызывающей программе, но он
может ничего не знать об остальной части программы и концентри-
ровать внимание на одной конкретной проблеме.
Есть и другие преимущества использования процедур. Процеду-
ру можно вызывать из различных точек программы и из других
процедур. Процедура составляется один раз, и код ее хранится в па-
мяти «в одном экземпляре», что позволяет сэкономить пространство
памяти и время программирования.
Поскольку процедуры можно ассемблировать или компилиро-
вать независимо, их допустимо писать на разных языках. Для ос-
новной части большой программы целесообразно воспользоваться
языком высокого уровня, но задачи, которые трудно поддаются опи-
санию на этом языке, можно реализовать на языке Ассемблера.
Процедуры для решения наиболее распространенных задач по-
мещаются в библиотеку программ и доступны многим программи-
стам. В таких библиотеках хранятся процедуры статистических
расчетов, операций ввода-вывода и множество других. Это позволя-
ет сэкономить не только время программирования, но и время на
изучение технической информации, необходимой для решения
проблемы.
Таким образом, применение процедур имеет следующие очевид-
ные преимущества:
1. Упрощается разработка, отладка и сопровождение модульных
программ; они становятся более понятными.
2. Допускается вызов процедур из нескольких точек программы,
что позволяет сэкономить пространство памяти и время программи-
рования.
3. Имеется возможность объединить процедуры, написанные на
разных языках, в одну программу.
4. Возможность хранения сложных и часто используемых про-
цедур в библиотеках делает их доступными многим программистам.
Проблемы реализации процедур. Вопрос о взаимодействии
между процедурами и вызывающими их программами довольно
сложен. Мы остановимся на некоторых проблемах, которые прихо-
дится решать, чтобы обеспечить успешную реализацию процедур.
Процедура
Вызывающая
Рис. 9.1. Выполнение процедуры
3
Возврат
179
На рис. 9.1 показан процесс управления, т. е. порядок выполне-
ния команд, при вызове процедуры. Последовательность образую-
щих процедуру команд хранится в памяти отдельно от вызывающих
ее программ. По завершении процедуры должна выполняться та
команда, которая в вызывающей программе следует за командой
вызова процедуры. В этой связи прежде всего возникают проблемы
разработки метода передачи управления процедуре (с соответст-
вующей модификацией РС) и возврата управления (посредством
восстановления РС) после ее завершения.
Обычно метки операторов и другие определенные пользователем
имена не имеют смысла вне той процедуры, в которой они заданы.
Эти имена оказываются локальными для модуля, где они специфи-
цированы. Отсюда вытекает, что одна процедура не может обра-
щаться по имени к данным и меткам команд, находящимся в другой
процедуре. Такое ограничение, по существу, является достоинством,
так как исключает возможности возникновения конфликтов из-за
имен, использованных где-то еще в большой программе. Кроме того,
у программистов появляется стимул для того, чтобы следовать чет-
ким и жестким правилам организации взаимодействия между про-
цедурами и разрабатывать понятные и простые в отладке програм-
мы. Однако необходимо сделать одно исключение из локальности
определений имен — позволить программе вызывать процедуру
по имени.
Многие достоинства модульных программ зависят от того, не
возникают ли при выполнении процедуры побочные эффекты. Обра-
тимся к рис. 9.1. Если вызывающая программа загружает данное
в общий регистр в секции кода 1, то оно должно сохраниться в ре-
гистре в секции кода 3. Однако процедуре для выполнения задачи
могут потребоваться некоторые регистры. Следовательно, необхо-
дим способ защиты исходного содержимого общих регистров на
йремя выполнения процедуры.
Еще одна проблема заключается в разработке удобных и гиб-
ких способов точного определения подлежащих обработке данных
и ее результатов. Данные, обрабатываемые процедурой, и получае-
мые после обработки результаты называются аргументами процеду-
ры. Процесс передачи процедуре входных аргументов с указанием о
том, где разместить результаты, называется передачей аргументов.
Таким образом, при организации взаимодействия процедур
возникает целый ряд проблем:
• передача управления процедуре,
• сохранение содержимого регистров,
• передача аргументов, '
• возврат к следующей команде в вызывающей программе.
Все эти проблемы усложняются еще и тем, что процедуры могут
быть вложенными-, т. е. одна процедура вызывает другую, которая
вызывает следующую и т. д. На рис. 9.2 представлена последова-
тельность действий при вызове второй процедуры. Вызов процедуры
180
Программа А
Процедура В
Процедура С
Рис. 9.2. Вложенные процедуры. Стрелки показывают последовательность вы-
полнения команд. (В пронумерованных программных сегментах могут быть цик-
лы, но их здесь можно не учитывать.)
С требует места в памяти для хранения содержимого регистров
для процедуры В, адреса возврата и информации об аргументах
процедуры С. Вместе с тем пока остается нужной точно такая же
информация по незавершенной процедуре В. Очевидно, что исполь-
зовать фиксированную область памяти для обмена информацией
между процедурами невозможно.
Совокупность соглашений, методов и инструкций, составляющих
основу организации взаимодействия между процедурами и вызы-
вающими программами, часто называются соглашениями о связи
процедур или, в терминологии фирмы DEC,— стандартом вызова
процедуры. Эти соглашения можно сравнить с большой головолом-
кой, в которой все части необходимо объединить в единое целое.
Связь процедур организуется различными методами, но, если ка-
кая-то часть подвергается модификации, соответствующие измене-
ния должны быть внесены и в другие части, чтобы не нарушить это
«единое целое». Конечно, сложность методов связи, обеспечиваемые
ими возможности и их особенности варьируются в разных ЭВМ.
Например, соглашения оказываются проще, если не допускаются
рекурсивные процедуры (т. е. процедуры, которые вызывают сами
себя). В общем случае, чем больше свободы предоставлено програм-
мисту, тем более детальными будут соглашения. В системе VAX
предусмотрены мощные и гибкие соглашения о связи, благодаря
которым упрощается применение таких сложных средств программи-
рования, как рекурсивные и реентрантные процедуры.
9.2. Стек
Что такое стек? Стек— это список элементов, в котором все
включения и все удаления (извлечения) осуществляются из его
вершины. Поскольку все включения и извлечения производятся из
вершины, следующий извлекаемый элемент всегда оказывается
последним включенным элементом. Стеки очень удобны для хране-
ния данных, обрабатываемых по принципу «последним вошел —
первым вышел» (L1FO).
181
Простран-
ство,
зарезерви-
рованное
для стека
а)
Низзверх
6)
Верх
Низ
в)
Рис. 9.3. Стек на различных этапах использования: а) пустой стек;
б) включено семь элементов; в) извлечено три элемента. Удобно уста-
навливать указатель низа на ячейку, расположенную ниже первой
ячейки стека, поэтому условие «низ = верх» означает, что стек пустой.
Стек организуется посредством выделения для него области па-
мяти и введения трех указателей. С помощью подвижного указа-
теля контролируется местоположение вершины, через которую вклю-
чаются и извлекаются элементы. Два фиксированных указателя
предотвращают извлечение элемента из пустого стека и включения
в стек большего числа элементов, чем он может вместить. Они указы-
вают соответственно на начало, или низ, стека и верхний предел
выделенной области.
Включение (push) элемента означает помещение его в стек, а
извлечение (pop) —удаление верхнего элемента1. Этот процесс
можно сравнить с хранением подносов в пружинном контейнере,
который применяется в кафетериях: когда поднос ставится в кон-
тейнер, он проталкивает остальные подносы вниз, а когда он удаля-
ется, другие подносы выталкиваются. При использовании стека дан-
ных в ЭВМ добавление и удаление элементов не вызывает переме-
щения остальных элементов, однако термины push и pop получили
очень широкое распространение.
1 Буквально: вталкивание (push) и выталкивание (pop).— Примеч. пер.
182
На рис. 9.3 показан пустой стек сразу после его образования,
а также после выполнения перечисленных выше операций1.
Стеки и процедуры. Стек представляется естественной струк-
турой для хранения информации, которую необходимо временно
запоминать при вызове процедуры. Объясняется это тем, что возврат
управления от процедур происходит в порядке, противоположном
порядку их вызова (здесь сохраняется принцип «последним вошел —
первым вышел»). Рассмотрим, например, адрес возврата — адрес
следующей команды в вызывающей программе, т. е. команды, кото-
рая должна выполняться после завершения процедуры. Когда про-
грамма А вызывает процедуру В, адрес команды, следующей за
командой вызова, включается в стек. Затем процедура В вызывает
процедуру С и адрес команды, находящейся после команды вызова,
в процедуре В вновь включается в стек над адресом возврата в
программу А. Возврат управления из процедуры С выполняется рань-
ше, чем возврат из процедуры В. Таким образом, по завершении
выполнения процедуры С адрес возврата, который она должна
использовать, оказывается включенным в стек последним — он на-
ходится в вершине стека.
Независимо от длины цепочки вызовов процедур адрес возврата
управления, необходимый для правильной передачи управления вы-
зывающей программе, включается в стек последним. По этой же
причине стек может служить для хранения содержимого регистров
и другой информации, запоминаемой при вызове процедуры.
Итак, чтобы возвратить управление вызывающей программе,
адрес возврата необходимо включить в стек. Но что же конкретно
подлежит включению в стек? Иными словами, как определить адрес
возврата? Во многих примерах гл. 8 было показано, что при выпол-
нении заданной командой операции в РС содержится адрес сле-
дующей команды. Следовательно, при вызове процедуры адрес
возврата находится в РС.
Пользовательский стек в системе VAX. Стеки применяются в
системе VAX для многих целей. Так, операционная система исподы-
зует стек для запоминания данных, необходимых при обработке
особых случаев. Регистр указателя стека kSP предназначен для
контроля за вершиной стека; он должен всегда содержать адрес
последнего включенного в стек элемента. Стек «растет» в- область
младших адресов зарезервированной для него памяти: первый
элемент находится в ячейке с наибольшим адресом. Как показано на
рис. 9.4, это позволяет представлять диаграмму стека естественным
образом: с вершиной вверху. (Операционная система инициализи-
рует SP на адрес байта, который находится перед областью, резер-
вируемой для стека, т. е. там, где показан указатель вершины
на рис. 9.3, а.)
1 Извлечение элемента из стека не разрушает содержимого его ячеек, поэтому
элементы 5, 6, 7 сохраняются в стеке.— Примеч. пер.
183
Младшие
адреса
Старшие
адреса
Рис. 9.4. Пользовательский стек в системе
VAX. В стек, показанный на рис. 9.3. в,
включены два элемента. (Отметим отсут-
ствие. указателя низа. Операционная сис-
тема VAX организует пользовательский
стек между v секциям и памяти, которые не-
доступны пользовательской программе,
поэтому включение или извлечение слиш-
ком большого числа элементов приводит
к возникновению особого случая.)
Пользовательский стек
может служить как времен-
ная рабочая память. Им явно
манипулируют команды
PUSH, POP и некоторые
другие. Нас же пока инте-
ресует в основном примене-
ние стека для связи проце-
дур. Пользовательским сте-
ком неявно оперируют коман-
ды вызова процедур и возв-
рата управления. По коман-
дам CALL (вызвать) и RET
(возвратиться) в стек авто-
матически включаются и ис-
ключаются из него такие
данные, как содержимое ре-
гистров и адрес возврата.
Внутренние подпрограм-
мы. Сделаем небольшое от-
ступление и рассмотрим ана-
логичную, но более простую
ситуацию. Кроме команд
для поддержки соглашений
о связи процедур, в системе
VAX (как и во многих других
больших ЭВМ) предусмотрены команды для решения одной из глав-
ных проблем обеспечения связи процедур — запоминания необходи-
мых данных в стеке.
Речь здесь идет о «вызове» последовательности команд из нес-
кольких точек внутри одного модуля. В терминологии фирмы DEC
такая последовательность команд называется подпрограммой.
Чтобы подчеркнуть ограничения на эти подпрограммы и отличие их
от подпрограмм в языке Фортран, назовем их внутренними под-
программами. Внутренние подпрограммы не имеют аргументов в
обычном смысле (хотя они и оперируют содержимым регистров,
которое при каждом вызове оказывается другим). Их нельзя ассем-
блировать отдельно от модуля и вызывать из других программ. До-
стоинства внутренних подпрограмм заключаются в том, что они
просты, имеют небольшое время выполнения и занимают незначи-
тельный объем памяти.
Единственной информацией, которая запоминается при вызове
внутренней подпрограммы, является адрес возврата. Этот адрес,
конечно, запоминается в стеке. Следовательно, если содержимое
какого-либо регистра модифицируется, его первоначальное содер-
жимое теряется. Из-за отсутствия списка аргументов внутренняя
подпрограмма всегда использует либо одни и те же данные (на-
пример, печать элементов конкретного массива на различных этапах
184
выполнения некоторой задачи), либо данные, загруженные для
обработки в конкретные регистры.
Внутренние подпрограммы реализуют следующие команды:
BSBB назначение 1 перейти к подпрограмме
BSBW назначение J
RSB возвратить управление из подпрограммы
В описаниях BSBx последняя буква показывает, используется
ли для кодирования смещения перехода байт или слово. По командам
BSBx адрес возврата для подпрограммы (PC) включается в стек,
а затем в PC загружается адрес назначения перехода. С помощью
команды RSB из стека просто извлекается верхнее длинное слово
и загружается в PC, что обеспечивает возврат из подпрограммы.
Конечно, для того чтобы команда RSB выполнялась правильно,
любые включенные в стек подпрограммой данные должны быть
извлечены из него до выполнения этой команды.
Пример 9.1. Внутренняя подпрограмма. Ниже приведен сегмент программы
с внутренней подпрограммой, преобразующей данные из дополнительного кода
в символьный. Отметим, что подпрограмма помещена после оператора EXIT, по-
этому она выполняется только при «вызове». Стек же используется в качестве
временной рабочей памяти. Так как помещенное в стек данное извлекается из
него (благодаря автоинкрементному спецификатору операнда в команде CVTLP),
по команде RSB из стека будет извлечен адрес возврата.
B8BW CVTWS
BSBW CVTWS
EXIT
I
I
) CVTWS
I
I Эта подпрограмма преобразует слово, адрес которого находится а регистр*
I R8, символьный код и запоминает полученнум цепочку по адресу ио реги-
) стра R1O. Предполагается, что регистр R9 содержит число требуемых о ре-
I эультате разрядов и что для промежуточного этапа зарезервирована область
4 в PKD.
I
CVTWSi CVTWL (R8),-(SP)
CVTLP <8P)+,R9,PKD
CVTP8 R9,PKD,R9,(RIO)
RSB
I Конец подпрограммы CVTWS
.END PROS
185
9.3. Стандарт вызова процедур
В разд. 9.1 подчеркивалось, что для установления правильных
соглашений о связи процедур приходится учитывать множество
деталей. Одни из требуемых операций задаются программистом
явно, другие же осуществляются CPU в ходе выполнения специ-
альных команд. Какие-то действия реализуются в процедуре, а
какие-то — в вызывающей программе. Все части головоломки дол-
жны быть собраны в единое целое и пока это не произойдет, возможно
недопонимание отдельных моментов. Чтобы помочь читателю разоб-
раться в существе рассматриваемого вопроса, ниже дается краткий
обзор стандарта вызова процедур системы VAX. Более детально
(с примерами) этот стандарт обсуждается в следующих трех
разделах. Здесь же описываются его различные этапы, команды
и правила в порядке их реализации.
Программист должен задать в вызывающей программе список
аргументов для процедуры. Список аргументов помещается в стек
или какую-то другую область памяти; весь список или его часть
могут быть установлены во время ассемблирования. При вызове
процедуры VAX загружает адрес списка аргументов в регистр АР —
указатель аргументов.
1?н- Процедуры вызываются по командам CALLG или CALLS, кото-
рые выполняют множество функций, невидимых программисту.
Они обеспечивают включение в стек содержимого регистров и другой
информации и загрузки новых данных в определенные регистры
(например, в АР и РС).
Процедура должна содержать маску входа, определяемую про-
.Р8ЕСТ имя.процедуры
Документация
Реоорвирование памяти
и инициализация, вели необходимо
•ENTRY имя_процедуры, маска.входа
Список использования регистров
Команды (использующие АР
для доступа к аргументам)
RET
• END
Рис 9.5. Организация процедуры
186
граммистом для указания используемых процедурой регистров.
Эта информация позволяет по командам CALL определить, содер-
жимое каких регистров подлежит запоминанию в стеке.
Процедура обращается к своим аргументам через АР. Для
этого особенно удобны два режима адресации: со смещением и кос-
венный со смещением.
По команде RET в процедуре осуществляется возврат управ-
ления вызвавшей ее программы. Из стека извлекается информа-
ция, которая была включена в него по команде CALL. По этой же
команде в регистры загружается их старое содержимое, которое
было сохранено в стеке.
Общая организация процедуры представлена, на рис. 9.5. Ди-
ректива .PSECT не обязательна, но по ней процедуре присваивается
имя, которое фигурирует в сообщениях об ошибках времени
выполнения. Имя помогает программисту определить процедуру,
в которой возникла ошибка. (Директива .PSECT подробно рассмот-
рена в гл. 10). В документацию'на процедуру должны быть включены
список аргументов с указанием их назначения и типов данных,
формулировка задачи и изложение метода ее решения, описания всех
специальных случаев и многое другое, что может представлять
интерес. Директива .ENTRY обсуждается в следующем разделе.
Директива .END является последним оператором в процедуре.
В ней запрещается указывать аргумент; адрес передачи управления
должен быть только в директиве .END основной программы.
9.4. Директива .ENTRY
Способы запоминания содержимого регистров. Одной из проблем,
возникающих при организации связи процедур, является защита
содержимого регистров, используемых вызывающей программой.
Существует несколько решений этой проблемы, но прежде чем
выбрать то или иное решение, нужно рассмотреть чётыре узловых
вопроса.
Первый вопрос: где запоминать старое содержимое регистров?
Для системы VAX ответ очевиден — в стеке. Однако следует от-
метить, что в некоторых ЭВМ нет «встроенного» стека. В таких
ситуациях стандарт вызова процедур требует, чтобы вызывающая
программа или процедура резервировала место для запоминания
содержимого регистров.
Второй вопрос: содержимое каких регистров необходимо запоми-
нать? Можно, конечно, на всякий случай запомнить значения всех
регистров, но это малоэффективно. Имеются две альтернативы:
запоминать содержимое регистров, используемых в вызывающей
программе, так как они могут потребоваться процедуре и будут
ею модифицированы, или тех регистров, которые используются
в процедуре, поскольку хранимые в них данные могут понадобиться
187
вызывающей программе. (Заметим, что в соответствии с принципом
независимости процедуры и вызывающей ее программы предполага-
ется, что ни одна из них «не знает», какие регистры используются
другой.) Обычно выбирается вторая альтернатива: старое содер-
жимое регистров, используемых в процедуре, сохраняется и возвра-
щается в них же после завершения процедуры. Одна из причин
такого выбора заключается в том, что вызывающая программа
может легко обращаться к процедуре, и при этом не возникают
никакие побочные эффекты. Процедура несет ответственность за
то, чтобы выполнить свою функцию без искажения данных вызы-
вающей программы. Еще одна причина выбора второй альтернативы
связана с сокращением объема требуемого пространства памяти.
Программа может вызывать несколько процедур и (по первому спо-
собу) перед каждой командой CALL должна выдавать сообщение
о том, какие регистры необходимо запоминать. При втором же
способе информация об используемых процедурой регистрах пред-
ставляется всего один раз в самой процедуре.
Наконец; третий вопрос: «кто» должен фактически запоминать
содержимое регистров — вызывающая программа или процедура?
Из ответа на предыдущий вопрос следует, что старое содержимое
регистров должна запоминать процедура; вызывающей программе
не известно, какие регистры нужно запоминать. Такое решение при-
нято во многих ЭВМ, но не в системе VAX: здесь старое содержимое
регистров запоминается с помощью команды CALL. Конечно, коман-
да CALL находится в вызывающей программе, но лучше считать,
что она действует как часть команды перехода от одной программы
к другой (по стрелке 2 на рис. 9.1).
Последний вопрос специфичен для системы VAX: поскольку
запоминается содержимое регистров, используемых в процедуре,
а фактически старое содержимое регистров запоминается по ко-
манде CALL, как же команда «узнает», какие регистры запоминать?
Необходимая информация содержится в директиве .ENTRY.
Директива .ENTRY. Хотя проблемы организации связи проце-
дур можно логически разделить, четко разделить их решения не-
возможно. Директива .ENTRY выполняет некоторые функции, по-
могающие решить две проблемы: запоминание содержимого реги-
стров и передачу управления процедуре. Она определяет точку
входа процедуры и задает маску входа, или маску запоминания
регистров, которая показывает, содержимое каких регистров необ-
ходимо сохранять.
Директива .ENTRY имеет следующий формат:
.ENTRY имя_процедуры, маска_регистров
Имя процедуры объявляется как точка входа, с которой начи-
нается выполнение процедуры. Оно должно быть отмечено для ре-
дактора связей как глобальное имя, т. е. имя, доступное из других
модулей. Его значением является местоположение директивы
188
.ENTRY, поэтому последняя должна находиться в начале испол-
няемой части процедуры.
Маска запоминания регистров представляет собой 16-битовый
набор, содержащий информацию о том, какие регистры исполь-
зуются в процедуре и какие прерывания в ней разрешены. Допус-
кается определять только регистры R2—R11. Для каждого из них
бит п в маске устанавливается в состояние 1, если определен регистр
Rn. Содержимое регистров R0 и R1 запоминать нельзя, так как по
соглашению они применяются для возвращения значений процедур-
функций. Регистр SP никогда не включается в стек, а регистры
АР, FP и РС всегда включаются в стек по команде CALL и опре-
делять их не нужно. Следовательно, несколько битов в маске можно
использовать для других целей. Биты 14 и 15 служат для установки
в PSW флагов разрешения прерываний IV и DV.
Программист определяет маску запоминания регистров в следую-
щей форме:
АМ <список регистров и прерываний^
Имена регистров и прерываний в списке должны разделяться
запятыми. Из указанных регистров и прерываний ассемблер форми-
рует 16-битовую маску. Регистры не обязательно указывать в числен-
ном порядке, но они должны присутствовать в списке. Маска поме-
щается в ячейку, где имеется директива .ENTRY (ячейку, которой
назначено имя имя____процедуры).
Пример 9.2. Маска запоминания регистров:
AM<R3,R7,R8,R9,IV>
Ассемблер построит следующую двоичную маску (напомним, что биты нуме-
руются справа налево, начиная с нуля):
0100001110001000
Пример 9.3. Директива . ENTRY:
.ENTRY OUTPUT, M<R2,R3,R6,R7,R10>
определяет имя OUTPUT как точку входа процедуры и показывает, что последняя
использует регистры R2, R3, R6, R7 и R10.
Важно, чтобы программист не пытался переложить свои обязан-
ности на ассемблер. Маска должна содержать все регистры (от R2
до R11), которые модифицирует процедура (включая и те, содержи-
мое которых изменяется по таким командам, как MOVC3). При
вызове процедуры запоминаемые регистры определяются только с
помощью маски. Если процедура изменила содержимое регистра,
не указанного в маске входа, его старое содержимое теряется, что
потенциально ведет к ошибкам после возврата управления вызываю-
щей программе.
Каким же образом по командам CALL отыскивается маска
регистров и определяются запоминаемые регистры? Оказывается,
имя процедуры (а точнее, имя точки входа) является операндом
команд CALL. Следовательно, по команде CALL можно получить
адрес маски запоминания регистров и легко просмотреть ее. Отметим,
что маска регистров занимает два байта и команда CALL ини-
189
циирует загрузку в РС адреса имя_процедуры+2, чтобы осущест-
вить передачу управления процедуре.
Первый аргумент директивы .ENTRY, хотя и называется
имя процедуры, далеко не всегда является именем процедуры.
Иногда удобно иметь для процедуры более одной точки входа.
В таких ситуациях директива .ENTRY вводится в тех местах
процедуры, где должна быть установлена точка входа, и все эти
места именуются по-разному.
9.5. Списки аргументов
Что такое список аргументов? Процедура и вызывающая ее про-
грамма взаимодействует через список аргументов. Данные, которыми
оперирует процедура, передаются ей как аргументы, а любые ре-
зультаты, возвращаемые ею в вызывающую программу, считаются
аргументами. (Процедуры-функции возвращают значение в регистре
R0 и, возможно, в регистре R1.) Следовательно, для решения проб-
лем организации связи процедур необходимо точно определить, что
находится в списке аргументов, каков его формат и где он разме-
щается.
Предположим, что процедура имеет три аргумента: два входных,
используемых ею в своей задаче, и один выходной, который она
вычисляет и. передает вызывающей программе. Самое простое ре-
шение — разместить два данных где-то в памяти, оставить после
них «пустое» место для результата выполнения процедуры и сооб-
щить ей (поместив адрес списка в АР), где находятся аргументы.
Конечно, такое решение в принципе приемлемо, но не во всех си-
туациях. Предположим, что одним из аргументов является массив.
Малоэффективно копировать весь массив в список аргументов, а
затем копировать измененные элементы в первоначальную об-
ласть. Точно так же нет смысла копировать в список аргументов
длинную символьную цепочку или любые данные, занимающие
большие объемы памяти. Наиболее общий механизм передачи
аргумента заключается в том, чтобы поместить
в список адрес данного, а не само данное.
Такой способ передачи аргументов называется
вызовом ссылкой. Аргументы можно передавать
и другими способами (они рассматриваются
ниже), но использование адресов удобно
и позволяет создать стандарт.
Стандартная форма списка аргументов
в системе VAX (для вызова ссылкой) приведена
на рис. 9.6. Каждая строка в таблице
обозначает длинное слово. Число аргументов
п представляется беззнаковым целым числом
в формате байта, поэтому 0^п^255. Адреса рис gg. формат
аргументов должны размещаться в том списка аргументов
190
00000003
NAMES
TAGS
NUM
порядке, в каком их ожидает процедура.
Создание списка аргументов во время ассемб-
лирования. Директива .ADDRESS. Директива
.ADDRESS предназначена для инициализации
данных, как и директивы .BYTE, .WORD и .LONG,
рассмотренные в гл. 4. Однако если последние
директивы инициализируют память на целые чис-
Рис. 9.7. Пример ла в дополнительном коде, то директива
списка аргументов .ADDRESS инициализирует память на адреса.
Она очень удобна при создании списков аргу-
ментов во время ассемблирования.
Формат этой директивы имеет вид:
.ADDRESS список_адресных_выражений
Элементы списка должны разделяться запятыми. Ассемблер по-
мещает адреса в память в указанном порядке, выделяя для каждого
адреса длинное слово. (Напомним, что ассемблер вычисляет значе-
ния имен относительно начала модуля или программной секции,
в которых они определяются. Если же процедура использует список
аргументов, в нем должны находиться фактические адреса аргумен-
тов. Необходимую коррекцию значений, запомненных ассемблером,
осуществляет редактор связей.)
• п
Пример 9.4. Список аргументов. Предположим, что вызываемая процедура
имеет три аргумента: два массива и данное в формате длинного слова, которое
сообщает, сколько элементов содержится в массивах. Если процедура оперирует
массивами NAMES и TAGS, a NUM показывает число содержащихся в них эле-
ментов, то список аргументов определяется следующим образом:
ARGLIST: .LONG 3 ; Число аргументов
.ADDRESS NAMES,TAGS,NUM ; Адреса аргументов
Определяемый этими директивами список аргументов представлен на рис. 9.7.
Конечно, имена NAMES, TAGS и NUM должны быть обычным образом опреде-
лены где-то в программе.
Часто во время ассемблирования можно создать только часть
списка аргументов, так как некоторые из них не известны до вре-
мени выполнения. Например, процедура А может вызывать другую
процедуру и передавать ей часть аргументов, формируемых про-
граммой, вызвавшей эту процедуру А. Кроме того, процедура может
вызываться неоднократно, возможно, в цикле, всякий раз с различ-
ными аргументами. В таких ситуациях допускается зарезервировать
место для списка аргументов и инициализировать те элементы,
которые не должны изменяться, а затем, во время выполнения,
пересылать в список те элементы, которые будут изменяться.
Пример 9.5. Список аргументов. Предположим, что процедура имеет четыре
аргумента: таблицу, ее размер, отыскиваемый элемент и место для возвращаемой
информации об отыскиваемом элементе. Процедура будет вызываться многократно
для поиска различных элементов. Список аргументов инициализируется следую-
щим образом:
191
ARGLSTi .LONG 4
.ADDRESS TABLE,SIZE
. BLKA 1
.ADDRESS INFO
I Число аргументов
; Первые два аргумента
I Место для третьего агрумента
I Последний аргумент
Если во время выполнения адрес отыскиваемого элемента находится в
регистре R8, то его можно передать в список аргументов следующей командой:
MOVL R8.ARGL1ST + 12 ;Ввести адрес третьего аргумента
Создание списка аргументов в стеке. Команды PUSH. Создава-
емый в памяти список аргументов, как и в приведенных- выше
примерах, является постоянным, т. е. занимаемое им место резер-
вируется на все время выполнения программы, даже если он тре-
буется лишь в коротком интервале. Рассмотренный в разд. 9.2
пользовательский стек предназначен для временного хранения и
весьма удобен для задания списков аргументов. После возврата
управления список удаляется из стека и его можно использовать по-
вторно. В' рекурсивных процедурах стек необходимо задействовать
для списков аргументов, поскольку при каждом вызове процедурой
самой себя ей требуется новый список аргументов, а списки для
предыдущих вызовов, из которых возврат еще не осуществлен, раз-
рушать нельзя.
Формат списка аргументов в стеке аналогичен формату списка
аргументов в любой области памяти (см. рис. 9.6). Программист
должен пользоваться командами для явного включения в стек адре-
сов аргументов. Первое длинное слово списка аргументов, содержа-
щее число аргументов, включается в стек автоматически по команде
CALLS.
В системе VAX предусмотрено несколько команд, применение
которых упрощает включение в стек адресов аргументов:
PUSHAx операнд jr = B,W,L или Q
PUSHL операнд
По команде PUSHA в вершину стека включается адрес операнда,
а по команде PUSHL в стек включается ее операнд в формате
длинного слова. Так как указатель стека содержит адрес вершины,
до пересылки данного в стек необходимо выполнить декремент SP
на четыре. Поэтому команды PUSH реализуют те же операции,
что и команды
MOVAx операнд, —(SP)
MOVL операнд, — (SP)
Однако команды PUSH короче и, возможно, понятнее приведен-
ных выше. Команды PUSHA всегда включают в стек 32-битовый
адрес. Как ив командах MOVA, важно учитывать тип х в мнемониках
команд, если в используемом для адресации операнда режиме
осуществляется инкремент или декремент регистра. Например, по
команде
192
PUSHAW (R7) +
адрес из регистра R7 включается в стек, а затем производится
инкремент регистра R7 на два.
Отметим, что адреса аргументов необходимо включать в стек в
порядке, противоположном тому,, в котором их ожидает процедура,
поскольку включенный первым аргумент оказывается нижним
(последним) в списке.
Пример 9.6. Включение списка аргументов в стек. Для создания в стеке списка
аргументов из примера 9.5 потребуются команды:
PUSHAB INFO
PUSHL R8
PUSHAL SIZE
PUSHAB TABLE
(Для второго и четвертого аргументов типы данных выбраны произ-
вольно.)
Использование аргументов в процедуре. При вызове процедуры
адрес списка аргументов загружается в регистр-указатель аргумен-
тов АР. Этот регистр содержит адрес первого байта списка, в ко-
тором указано число аргументов. Для доступа к элементам списка
целесообразно воспользоваться режимом смещения с регистром АР.
Ячейками с адресами первого, второго и третьего аргументов будут
4(АР), 8(АР) и 12(АР) соответственно. Для адресации самих ар-
гументов, т. е. для получения из памяти собственно данных, удобен
косвенный режим со смещением. Напомним, что в этом режиме
определяемый спецификатором операнда адрес является адресом
адреса операнда, поэтому он наиболее часто применяется для до-
ступа к аргументам процедур. Например, 4 (АР) есть адрес адреса
первого аргумента, а для доступа к самим данным необходимо запи-
сать @ 4(АР).
Содержимое АР и списка аргументов процедура должна рас-
сматривать как данные, которые можно только считывать. Другими
словами, при обращении к своим аргументам процедура не должна
модифицировать АР, изменять данные в списке аргументов или запо-
минать в нем результаты, возвращаемые в вызывающую программу.
Пример 9.7. Список аргументов и процедура поцска. На рис. 9.8
показана процедура поиска, имеющая четыре аргумента, а на рис. 9.9 — список
аргументов, который необходимо использовать при вызове процедуры. Стрелки на
рис. 9.9 идентифицируют ячейки памяти, «указываемые» адресами. Отметим, что
в команде MOVL, на рис. 9.8 для выборки из списка аргументов адреса первого
аргумента (адреса массива) применяется режим смещения. В следующих двух
.PSECT SEARCH
5
j ПРОЦЕДУРА SEARCH (ARRAY, NUM, ITEM, LOC)
I
j Эта процедура отыскивает указанный элемент о массиве слов.
7 Зак. 821
193
I В ней применяется последовательный поиск.
I
| ВХОДНЫЕ АРГУМЕНТЫ
I
3 ARRAY массив слов
I NUM число элементов (слово)
I ITEM отыскиваемый элемент
I
| ВЫХОДНОЙ АРГУМЕНТ
I LOC адрес найденного элемента
I ( будет О, если элемента в массиве нот)
I
I Смещения для списка аргументов
ARRAY • 4
NUM - 8
ITEM - 12
LOC - 16
1 .ENTRY SEARCH y *M<R6,R7,R8>
1 | ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ! R6 указатель ARRAY 3 R7 индвкс цикла 3 R8 ITEM 1 MOVL ARRAY.(АР),R6 3 Ваять адрвс массива
1 CVTWL BLEQ MOVW QNUM(AP),R7 3 Передать NUM как длинное слово NOTFND 3 Перейти, если массив пустой SITEM(AP),R8 3 Взять ITEM
COMPR1 CMPW BEQL SOBGTR R8,(R6)+ 3 Сравнение и инкремент указателя FOUND 3 Перейти, если элемент найден R7,COMPR 3 Управление циклом
NOTFNDi CLRL RET BLOC(АР) з Запомнить О, ITEM но найден 3 Возврат
FOUND! MOV AW RET .END —(R6),BLOC(АР) з Адрес ITEM в LOC 3 Возврат Рис. 9.8. Процедура поиска
командах указывается косвенный режим со смещением, т. е. элемент в списке
аргументов является адресом операнда и для считывания операнда из памяти к
ней осуществляется второе обращение. Так, с учетом списка аргументов на рис. 9.9
спецификатор операнда-источника @NUM (АР) в команде CVTWL адресует число
50ю в KNT. Спецификатор операнда @1ТЕМ (АР) в следующей команде адресует
194
LIST
(АР)
— 4 (АР)
— 8 (АР)
— 12 (АР)
—- 16 (АР)
Рис. 9.9. Список аргументов для процедуры на рис. 9.8
число— 134ю в ALPHA. По команде MOVAW в конце процедуры с помощью кос-
венного режима со смещением результат запоминается в ячейке памяти LOC, адре-
суемой четвертым аргументом, а не в самом списке аргументов.
На рис. 9.8 показано несколько полезных приемов документиро-
вания. Так как аргументы передаются через список аргументов,
в процедуре к ним нельзя обращаться по именам. Здесь введены
«фиктивные» имена аргументов, которые применяются для двух раз-
личных целей. Во-первых, часто удобно именовать аргументы, чтобы
пользоваться именами в общем описании процедуры и в комментари-
ях команд. Во-вторых, обращения к списку аргументов становятся
более понятными, если определить имена, значениями которых слу-
жат необходимые для доступа к каждому аргументу смещения от
АР. Например, имея определение NUM = 8, можно записать
CVTWL @NUM(AP), R7
вместо менее понятной записи
CVTWL @8(АР), R7
Таким образом, программист видит, к какому аргументу осуще-
ствляется обращение, не запоминая и не просматривая формат спис-
ка аргументов.
Отметим также, что первая строка комментария похожа на опе-
ратор заголовка процедуры в языке высокого уровня. Эта строка
идентифицирует процедуру и определяет порядок аргументов в списке
аргументов.
Пример 9.3. Передача аргумента из одной процедуры в другую. Предположим,
что основная программа вызывает процедуру А, имеющую три аргумента, а она
7* 195
Основная программа
Процедура А
Рис. 9.10. Передача аргументов из одной процедуры в другую
в свою очередь вызывает процедуру В, которая имеет два аргумента. Рассмотрим,
как процедура А может передать один из своих аргументов, например первый,
процедуре В в качестве ее второго аргумента (рис. 9.10). Процедура А формирует
список аргументов для процедуры В в стеке. Она же обращается к своим аргумен-
там через АР. Поэтому для включения в стек второго аргумента процедуры В
применяется команда:
PUSHL 4 (АР)
Следующим процедура А включает в стек адрес первого аргумента процедуры В,
а затем вызывает эту процедуру. 1
Другие способы передачи аргументов. Во всех списках аргу-
ментов, рассматриваемых в настоящем разделе, аргументы переда-
ются с помощью ссылок. Другими словами, список аргументов со-
держит их адреса. Как уже отмечалось, этот способ передачи
аргументов является удобным и общим, но существуют еще более
удобные способы. Согласно одному из них вызывающая программа
помещает в список аргументов само значение аргумента. В тер-
минологии фирмы DEC этот способ называется механизмом непо-
средственного значения. В списке аргументов каждый аргумент
всегда представлен длинным словом независимо от того, передается
он ссылкой или непосредственным значением. Такой механизм вполне
приемлем для констант и простых переменных, которые процедура
не изменяет. Его нельзя использовать для выходного аргумента
процедуры, так как процедура никогда ничего не запоминает в
списке аргументов.
196
31 23 15 О
Класс Тип Длина
Адрес
Рис. 9.11. Дескриптор аргумента. Здесь
класс и тип — целые числа (байты), кото-
рые описывают тип данных аргумента.
(Подробнее об этом см. в руководстве
VAX Architecture Handbook.)
В системе VAX применяется
еще один механизм, называе-
мый механизмом дескриптора.
Он предназначен для передачи
цепочек (символов, упакован-
ных десятичных чисел й т. п.)
и других типов данных, полное
описание которых требует не
только адреса. Например, сим-
вольная цепочка описывается
своими адресом и длиной,
упакованное десятичное число — адресом и числом разрядов. Для
передачи аргумента дескриптором вызывающая программа фор-
мирует в памяти дескриптор аргумента и помещает его адрес в список
аргументов. Стандартный формат дескриптора приведен на рис. 9.11.
Вместо передачи аргумента-цепочки дескриптором можно помес-
тить в список аргументов адрес цепочки и адрес ее длины с помощью
двух передаваемых ссылкой аргументов точно так же, как в машин-
ных командах для описания таких типов данных применяются
два операнда.
Конечно, если аргументы передаются посредством различных ме-
ханизмов, вызывающая программа и процедура должны распозна-
вать, какой механизм используется для каждого аргумента. Доступ
к аргументу в процедуре должен обеспечиваться различными режи-
мами адресации в зависимости от того, как он передан. Например,
для первого аргумента можно применить следующие спецификаторы
операндов:
4(АР) передам непосредственным онемением
04(АР) передан ссылкой
04(R0) после выполнения команды MOVL 4(АР),RO
для получения адреса дескриптора, если аргумент
передан дескриптором
Если и вызывающая программа, и процедура разрабатываются
одним программистом, он может выбрать наиболее удобный ме-
ханизм. Если же программа находится в библиотеке или формиру-
ется компилятором языка высокого уровня, программист должен
знать, какой механизм в ней использован.
9.6. Вызов процедуры
и возврат управления программе
Кадр вызова. Как указывалось ранее, при вызове процедуры
команды CALLS и CALLG включают в пользовательский стек
значительную информацию о связи. Эта информация, представлен-
197
Адрес
обработчика условия или О
Другие
Маска
регистров
Старое PSW
F Р (новое значение,
установленное
командами CALLG
или CALLS)
Старый (АР)
Старый (FP)
Старый (РС)
(Запомненный регистр
с наименьшим номером)
I
Другие данные в стеке
(Запомненный регистр
с наибольшим номером)
Содержимое регистров,
определенных
маской входа
Рис. 9.12. Формат кадра вызова
ная в стандартном формате, называется стековым кадром или кад-
ром вызова. При возврате управления программе данные в кадре
вызова необходимо локализовать и удалить из стека. Поскольку
процедура может поместить в стек другие данные (включая, воз-
можно, стековый кадр вызываемой ею другой процедуры), указа-
тель стека модифицируется и не может служить для локализации
кадра вызова. Для этой цели предназначен регистр-указатель кадра
FP. При вызове процедуры в него помещается адрес кадра вызова,
а при возврате управления FP используется для локализации
запомненной в стеке информации о связи.
Выше было показано, что н стеке запоминаются адрес возврата
[ (РС) при вызове процедуры] и содержимое регистров, определен-
ных в маске входа процедуры. Рассмотрим теперь некоторые другие
данные, которые должны быть частью кадра вызова.
При вызове процедуры адреса списка аргументов и кадра вызова
помещаются соответственно в регистры АР и FP. Если процедура,
например А, вызывает другую процедуру, например В, в регистры
АР и FP будут загружены адреса списка аргументов и кадра вызо-
ва для процедуры В. Однако процедуре А может потребоваться
ее список аргументов, а ее кадр вызова потребуется, позже. Следо-
вательно, когда бы ни вызывалась процедура, текущее содержимое
регистров АР и FP должно быть запомнено до его модификации,
т. е. содержимое регистров АР и FP должно быть частью кадра
вызова.
Содержимое регистров, определенных в маске входа процедуры,
198
запоминается в стеке, а позднее должно быть извлечено и возвращено
в регистры. Каким же образом команда RET «узнает», которые из
регистров запоминались при вызове? Очевидно, кадр вызова должен
содержать копию битов 11:0 маски запоминания регистров.
Формат кадра вызова представлен на рис. 9.12 (некоторые из
показанных на рисунке данных поясняются ниже).
Вызов процедуры. Команды вызова процедуры CALLG и CALLS
формируют кадр вызова и передают управление процедуре. Коман-
да CALLG применяется в том случае, когда в стек включены адреса
аргументов, а команда CALLS — когда список аргументов сформи-
рован где-то в памяти. Форматы команд вызова:
CALLG список__аргументов, имя_процедуры
CALLS число___аргументов, имя_процедуры
Первым операндом команды CALLG указан список аргументов,
т. е. спецификатор операнда, показывающий адрес списка аргумен-
тов. В команде CALLS предполагается, что адреса аргументов
находятся в верхней части стека. Вторым операндом обеих команд
CALL является имя процедуры.
Пример 9.9. Вызов процедур
а) С помощью команды CALLS
Xs .BLKL 1
Ys .BLKL 1
Zi .BLKL 1
ARGLSTs .LONG 3
.ADDRESS X,Y,Z
CALLG ARGLBT.ADD
б) С помощью команды CALLS
X: .BLKL 1
Ys .BLKL 1
Zs .BLKL 1
199
PUSHAL
PUSHAL
PUSHAL
CALLS
Z
Y
X
#3,ADD
Команды CALLG и CALLS — очень мощные. Во многих ЭВМ
программист должен проделать значительную работу, которая с
помощью этих команд выполняется автоматически. Действия обеих
команд почти одинаковы. Ниже они подробно описаны и указаны
имеющиеся между ними незначительные различия.
Команда CALLG. Эта команда инициирует выполнение следую-
щих операций:
1. Выравнять SP на границу длинного слова.
По командам CALL в стек включаются несколько длинных
слов и для большей эффективности сначала выравнивается SP на
границу длинного слова. Выравнивание осуществляется путем уста-
новки двух правых битов SP в нуль (рис. 9.13). Исходные значения
Предположим, что в стек включается байт данных, который может
быть не выравнен на границу длинного слова.
=75Е06«.. .0110 (двоичное)
До включения каждого длин-
ного слова кадра вызова
производится декремент (SP)
на 4.
Сброс правых двух бит изме-
няет (SP) на .. .0100=
75Е04, поэтому новый (SP)
указывает сюда.
Рис. 9.13. Выравнивание стека на длинное слово
двух правых битов запоминаются (в стеке на этапе 5), поэтому
при возврате управления программе по команде RET указатель
стека восстанавливается.
2. Включить в стек содержимое регистров, определенных в
маске входа процедуры.
Анализируется слово по адресу, определяемому именем имя_про-
цедуры; оно интерпретируется как маска запоминания регистров.
Запоминаемые регистры включаются в стек в порядке уменьшения
их номеров (поэтому при просмотре стека сверху вниз они оказы-
ваются размещенными в естественном порядке).
3. Включить в стек содержимое регистров PC, FP и АР.
4. Сбросить коды условий в PSW.
200’
31:30 29 27____________16 15_____________________________0 Номера
Маска PSW
I-------------- Биты 11:0 маски запоминания регистров
------------------------- Не используется
------------------------- Флаг CALLG/CALLS, установлен в 0
для команды CALLG И в 1 для команды
CALLS; используется командой RET.
------------------------- Правые два бита (SP) до его выравни-
вания на длинное слово.
Рис. 9.14. Второе длинное слово в кадре вызова
5. Включить в стек PSW, маску регистров и т. д.
В стек включается длинное слово, содержащее следующие дан-
ные: PSW (со сброшенными кодами условий), часть маски реги-
стров, которая потребуется позднее (биты 11:0), флаг, показываю-
щий, что процедура вызвана командой CALLG и правые два бита
первоначального адреса из SP. Формат этого длинного слова пред-
ставлен на рис. 9.14.
6. Включить в стек длинное слово, состоящее из нулей.
Процедура может запомнить в этом длинном слове адрес под-
программы обработки условия, которая выполняется, если в про-
цедуре возник особый случай.
7. Скопировать текущее содержимое SP в регистр FP.
До выполнения этой операции SP содержит адрес последнего
включенного в стек длинного слова. Он служит началом кадра
вызова для этого вызова процедуры; адрес его передается в регистр
FP.
8. Передать в регистр АР адрес списка аргументов.
Адрес списка аргументов является адресом первого операнда
в команде CALLG.
9. Установить биты прерываний в PSW.
Состояния битов IV и DV устанавливаются в соответствии с
маской запоминания регистров.
10. Передать в РС адрес имя__процедуры +2.
Поскольку слово по адресу имя_процедуры оказывается маской
запоминания регистров, адресом, по которому должно начаться
выполнение процедуры, будет имя__процедуры +2. Загрузка этого
адреса в РС вызывает передачу управления процедуре. Действие
команды CALLG на этом заканчивается, и CPU может обычным
образом выбирать следующую команду по адресу из РС.
Команда CALLS. В команде CALLS предполагается, что адреса
аргументов находятся в вершине стека. Поэтому прежде всего с
помощью этой команды в стек включается длинное слово, содер-
201
жащее число аргументов (ее первый операнд), чтобы завершить
список аргументов. Модифицированное значение SP запоминается
в рабочей ячейке для использования на этапе 8.
Остальные операции, инициируемые командой CALLS, почти
идентичны приведенным выше для команды CALLG. Различие между
ними состоит в том, что на этапе 5 по команде' CALLS флаг
CALLG/CALLS устанавливается в единицу, показывая тем самым
применение команды CALLS, а на этапе 8 передаваемый в АР адрес
будет значением SP, запомненным в рабочей ячейке сразу после
включения в стек числа аргументов.
Возврат управления программе. Для возврата управления пред-
назначена команда RET, не имеющая операндов. По существу, ее
действия противоположны действиям команд CALLG или CALLS,
т. е. она удаляет из стека кадр вызова и восстанавливает исходное
содержимое запоминаемых регистров. Если процедура вызывалась
командой CALLS, то по команде RET из стека извлекается список
аргументов.
При выполнении команды RET указатель стека SP не обязательно
указывает на начало кадра вызова. Если процедура включала в
стек данные и не извлекала их, SP адресует верхнюю ячейку стека
(с наименьшим адресом). Однако регистр FP должен всегда указы-
вать на кадр вызова. Очень важно, чтобы программист не модифи-
цировал содержимое FP. Команда RET инициирует выполнение
следующих операций:
1. Передать (FP)+4 в SP.
При этом из стека извлекается все, что в него включила про-
цедура, а также первое длинное слово кадра вызова, которое со-
держит адрес подпрограммы обработки условия.
2. Извлечь из стека следующее длинное слово и запомнить его
в рабочей ячейке.
Это длинное слово содержит PSW, часть маски запоминания
регистров, флаг CALLG/CALLS и два правых бита адреса, который
был в SP до вызова процедуры.
3. Извлечь из стека следующие три длинных слова и скопировать
их в регистры АР, FP и PC.
4. Извлечь из стека и восстановить содержимое запомненных
регистров.
Для определения запомненных в стеке регистров анализируются
биты 27:16 рабочей ячейки, которая была загружена на этапе 2.
Из стека извлекается необходимое число длинных слов, которые
загружаются в регистры в порядке увеличения их номеров.
5. Восстановить (SP).
До выполнения этой операции кадр вызова извлечен из стека
и SP адресует следующее длинное слово в стеке. Биты 31:30 рабочей
ячейки прибавляются к содержимому SP для перевода его в исходное
состояние перед вызовом процедуры (см. рис. 9.13).
202
6. Загрузить в PSW биты 15:0 рабочей ячейки.
7. Если процедура вызывалась командой CALLS, необходимо
извлечь из стека список аргументов.
Для определения использованной ранее команды вызова анали-
зируется бит 29 рабочей ячейки. Если он находится в состоянии 1,—
применялась команда CALLS и в стеке еще находится список аргу-
ментов. Адресуемый SP байт содержит число аргументов, напри-
мер п. Производится инкремент SP на 4*(п+1), что эквивалентно
удалению из стека длинного слова с числом аргументов и адресов
всех аргументов.
По завершении выполнения команды RET CPU обычным образом
выбирает и выполняет следующую команду. Так как в РС загружен
адрес, который был в нем при выполнении команд CALLG или
CALLS, следующей оказывается команда, находящаяся в памяти
после CALLG или CALLS.
Возврат значений и флагов из процедур. По соглашению, при-
нятому в системе VAX, процедура-функция возвращает значение
в регистре R0. Если же им является тетраслово или число двойной
точности с плавающей точкой, значение возвращается в регистрах
R0 и R1. Независимо от вида процедуры желательно, чтобы она
возвратила один или несколько флагов. Флаги показывают, успешно
ли процедура выполнила свою задачу, не возникли ли какие-то особые
случаи и т. п. Особенно желательно возвращение флагов из про-
цедуры, которая осуществляет ввод (или обрабатывает входные
данные), так как в ней может создаться много ошибочных ситуаций.
Например, макрокоманда READRCRD вызывает процедуру, кото-
рая возвращает флаг, показывающий достижение конца файла
DATA. DAT.
Флаги удобно возвращать в двух местах: в регистре R0 и в
кодах условий. (В процедуре-функции для флагов можно использо-
вать регистр R1.) В системе VAX проще всего воспользоваться реги-
стром R0, так как для проверки флага после возврата из процедуры
предусмотрены команды BLBS и BLBC [(перейти, если младший
бит установлен (S) или сброшен (С)]. Если бы таких команд не было
(во многих ЭВМ их нет), потребовалась бы явная проверка реги-
стра R0. В этом случае предпочтительнее воспользоваться кодом
условия, так как для проверки его достаточно одной команды ус-
ловного перехода. По командам CALL в системе VAX коды условий
всегда сбрасываются в копии запоминаемого в стеке старого PSW,
поэтому программист должен лишь установить один из кодов усло-
вий в запомненном PSW, если фиксируемое флагом условие удов-
летворяется. В разд. 9.8, например, рассмотрены процедуры, кото-
рые возвращают флаг посредством установки одного из кодов
условий.
203
9.7. Связь процедур
с программами, написанными
на языках высокого уровня,
и библиотечными процедурами
В разд. 9.1 при обсуждении преимуществ применения процедур
отмечалось, что в программе могут быть процедуры, написанные
на различных языках, и что программа может воспользоваться
библиотечными процедурами. До сих пор соглашения о связи рас-
сматривались в предположении, что вызывающая программа и про-
цедура написаны прикладным программистом на языке Ассемблера.
Теперь же мы обсудим связи процедур с программами, написанными
на языках высокого уровня (Фортран, Паскаль, Кобол), и с биб-
лиотечными процедурами системы VAX (VAX-11 Run-Time Procedure
Library).
Соглашения о связи процедур в системе VAX разработаны таким
образом, чтобы обеспечить без значительных усилий связь между
программами, написанными на различных языках. Большинство ком-
пиляторов языков высокого уровня системы VAX транслирует про-
цедуры и команды вызова процедур в соответствии с рассмотренными
в настоящей главе соглашениями о связи. Оператор CALL языка
высокого уровня транслируется в команду CALLS или CALLG,
поэтому вызываемая программой на языке высокого уровня ассемб-
лерная процедура должна обычным образом задать свою маску ре-
гистров, использовать регистр АР для доступа к своим аргументам
и возвратить управление вызывающей программе командой RET.
С другой стороны, компиляторы языков высокого уровня формируют
маски регистров при трансляции процедур на языках высокого
уровня, преобразуют ссылки на аргументы в спецификаторы операн-
дов через регистр АР и осуществляют возврат по команде RET.
Следовательно, такие процедуры допускается вызывать из ассемб-
лерных программ. Единственная проблема, которая при этом воз-
никает, связана с мехайизмом передачи аргументов.
В компиляторах языков высокого уровня и библиотеке процедур
времени выполнения заложен стандартный формат списка аргумен-
тов, представленный на рис. 9.6, за исключением того, что они
не всегда передают аргументы или ожидают получить свои аргумен-
ты с помощью ссылки, т. е. списки могут не содержать адресов
аргументов. В зависимости от конкретного языка и типа аргумента
иногда применяются непосредственные значения и дескрипторы.
Поэтому необходимо знать, каким образом действуют конкретные
компиляторы. Ниже кратко рассмотрены стандарты компиляторов.
Более подробные сведения можно найти в руководствах для поль-
зователя по различным языкам программирования.
Компилятор Фортрана системы VAX обычно использует вызов
с помощью ссылки для всех аргументов, кроме символьных цепочек,
204
где он пользуется дескрипторами. Если, например, программа на
Фортране содержит оператор
CALL SUB1 (LIST, N, 25)
компилятор сформирует список аргументов примерно следующими
командами:
ARSLSTi .LONS з
.ADDRESS LI8T,N,C0N8T2S
C0NST23I .LONS 23
(Длинное слово, содержащее число 25, не является частью списка
аргументов.) Для обеспечения интерфейса с ассемблерной процеду-
рой, не соответствующей стандарту Фортрана, программист может
явно сообщить компилятору Фортрана, каким образом вызывающая
программа должна передать аргумент. В соответствии с функциями
языка Фортрана %VAL, %REF и %DESCR в системе VAX в список
аргументов загружаются значения аргумента, его адрес или адрес
дескриптора. Например, оператор
CALL SUB2 (%REF(STR(NG),LENGTH)
заставит компилятор поместить адрес STRING (символьной цепоч-
ки) в список аргументов вместо того, чтобы построить дескриптор
для STRING и загрузить его адрес в список аргументов.
Подпрограмма Фортрана ожидает, что ее список аргументов
содержит адреса аргументов «не цепочек» и адрес дескриптора ар-
гумента-цепочки.
Компилятор Паскаля системы VAX использует механизм ссылок
для параметров-значений и параметров-переменных. Поскольку пе-
ременная, передаваемая как параметр-значение, не должна изме-
няться, процедура должна скопировать ее значение в рабочую ячейку
и использовать в своих вычислениях эту ячейку, а не фактический
адрес параметра. Для процедур на языке Паскаль компилятор реа-
лизует такое копирование, но если процедура написана на языке
Ассемблера, программист должен явно образовать копию пара-
метра.
Если ассемблерная процедура, вызываемая из Паскаль-програм-
мы, ожидает передачи аргумента механизмом, отличным от механиз-
ма ссылки, в списке формальных параметров объявления процедуры
в этой программе должен фигурировать соответствующий специфи-
катор. Спецификаторами служат %IMMED для механизма непосред-
ственного значения, %DESCR для дескрипторов массивов и скаляров
и %STDESCR для дескрипторов цепочек. Рассмотрим, например,
объявление следующей процедуры:
procedure proc (a i integer|
• ver result i integer।
Ximmed b i real)
Xstdescr string s alfa ) । extern
205
Список аргументов будет содержать адреса первых двух аргумен-
тов (но аргумент а должен быть скопирован ассемблерной процеду-
рой proc), текущее значение третьего аргумента и адрес дескриптора
(сформированного компилятором Паскаля) последнего аргумента.
Оператор extern необходим для того, чтобы показать, что для
Паскаль-программы процедура является внешней.
Ассемблерная программа, вызывающая Паскаль-процедуру, дол-
жна поместить адреса аргументов в список аргументов обычным
образом, за исключением некоторых типов данных, которые переда-
ются Паскаль-процедурам другими способами (такими исключе-
ниями являются динамические массивы, процедуры и функции;
сведения о них можно почерпнуть из соответствующих руко-
водств) .
Компиляторы Кобола системы VAX-11 в качестве стандартного
способа передачи аргументов используют вызов с помощью ссылки.
Программист может отменить стандарт, написав BY VALUE или
BY DESCRIPTOR перед именем аргумента в операторе CALL.
Библиотека процедур времени выполнения системы VAX-11 пред-
ставляет собой набор ассемблерных утилит, которые могут вызы-
вать программы, написанные на языках высокого уровня и Ассембле-
ра. В нее включены процедуры распределения памяти, ввода-вывода,
математических функций, преобразований типов данных, обработки
ошибок и др. Обычно аргументы-цепочки передаются в них
дескрипторами, а некоторые аргументы — непосредственными значе-
ниями. Многие из перечисленных процедур возвращают флаги в
регистре R0. Более подробно процедуры и форматы аргументов
описываются в руководстве VAX-11 Run-Time Library Reference Ma-
nual.
9.8. Манипуляции
связанными списками (пример)
Здесь мы рассмотрим пример программы, которая использует
10 процедур. Многие из них вызывают другие процедуры и должны
передавать ряд своих аргументов вызываемым процедурам. В этом
примере показана организация умеренно сложной программы с глу-
биной вызовов процедур до четырех уровней. Пример иллюстрирует
и некоторые дополнительные аспекты разработки программ. Боль-
шинство процедур реализует довольно общие операции и написано
так, что доступны для применений с различными форматами дан-
ных. Все операции, зависящие от конкретного формата данных, со-,
средоточены в небольшом числе очень коротких процедур. Следо-
вательно, все зависящие от данных операции локализованы и их
легко изменить. Для облегчения понимания и упрощения модифика-
ции (без ошибок) процедур символические имена введены для кон-
стант, зависящих от формата данных, и даже для смещений от
206
(АР), которые применяются для обеспечения доступа к аргументам.
Документация большинства процедур включает алгоритм, написан-
ный на языке высокого уровня.
Программа манипулирует связанным списком, представляющим
собой весьма удобную структуру данных.
Связанные списки. Во многих рассмотренных выше примерах и
упражнениях предписывалось хранить данные в массиве или табли-
це. Элементы находились в последовательных ячейках памяти.
Иногда представлять список данных подобным образом неудобно
или неэффективно. Если, например, желательно хранить данные в
численном или алфавитном порядке, но приходится часто добав-
лять или удалять элементы, то вряд ли имеет смысл всякий раз
переупорядочивать список. Связанные списки позволяют разделить
логический порядок элементов и их физическое размещение в па-
мяти. В связанном списке каждый элемент имеет поле указателя,
содержащее адрес логически следующего элемента, хотя физиче-
ски он может находиться в любой ячейке памяти. Группу байтов,
которые образуют один элемент связанного списка, включая указа-
тель и все поля данных, часто называют узлом (node).
Поскольку в связанном списке даже логически первый узел
может находиться в любом месте памяти, необходимо следить
за его адресом. Зная, где находится первый элемент, можно
найти и другие с помощью указателей. Следовательно, с любым
связанным списком ассоциируется йеременная-указатель, например
first, адресующая первый узел. Поле указателя логически послед-
него узла содержит специальное значение, называемое пустым ука-
зателем (часто оно представляется нулем). Наличие пустого указа-
теля означает, что узлов больше нет. Диаграмма связанного списка
представлена на рис. 9.15. Предполагается, что поле указателя явля-
ется первым полем в каждом узле.
Со списком часто приходится выполнять две операции: введение
(вставка) новых элементов и удаление имеющихся. В массиве эти
операции требуют пересылок множества данных. Однако в связанном
списке достаточно изменить значения всего одного или двух ука-
зателей (рис. 9.16). Чтобы стандартизовать операции по введению
207
Рис. 9.16. Введение и удаление узлов: а) введение узла, б) уда-
ление узла
и удалению узла, целесообразно иметь в начале списка фиксиро-
ванный узел, который не содержит элемента и никогда не удаляется
(пока не окажется ненужным весь список). Такой узел называется
головным. Благодаря ему исключается возникновение специальных
случаев изменения first при добавлении в начало списка нового
элемента или удалении первого элемента. В головном узле может
содержаться такая информация о списке, как его имя или число
текущие элементов.
Для того чтобы добавить в связанный список новый элемент,
необходимо найти незанятую область памяти. Обычно для узлов
связанного списка (возможно, нескольких связанных списков) ре-
зервируется большая область памяти. Она делится на единицы,
соответствующие размерам узлов, и эти единицы или пустые узлы
организуются в связанный список свободных узлов. Если программе
необходимо построить новый узел, она должна взять пустой узел
из списка свободных узлов. Если же элемент удаляется из связанного
списка, занятое им место необходимо утилизировать, т. е. узел
необходимо вернуть в список свободных узлов для повторного ис-
пользования при введении дополнительных элементов. Поскольку
порядок размещения пустых узлов особой роли не играет, узлы уда-
ляются и возвращаются в начало списка свободных узлов. Головной
узел в этом списке не нужен.
208 * •
Обозначения. Для описания алгоритмов, оперирующих связан-
ными списками, необходимо ввести некоторые обозначения, позво-
ляющие обращаться к различным полям произвольных узлов в спис-
ках. Каждый узел в списке должен иметь один и тот же формат,
поэтому полям можно присвоить имена. Итак, обращаться к конк-
ретному полю в конкретном узле будем следующим образом:
ptr*field_пате (указатель* имя_поля)
где ptr — указатель нужного узла. Тогда firsflink обозначает
поле связи (указатель) узла, адресуемого first. Значением firsClink
является содержимое этого поля, т. е. адрес следующего узла.
Пример. В рассматриваемую программу включены процедуры
для введения, удаления и поиска элементов, а также печати всех
элементов связанного списка, который организован (логически)
в численном или алфавитном порядке. Считается, что поде указателя
является первым полем в узле, но никаких предположений о числе,
типе или размере других полей не делается. Следовательно, процеду-
ры, приведенные на рис. 9.17, пригодны для самых разнообразных
применений.
На рис. 9.18 представлены три процедуры, зависящие от данных.
Они сравнивают, заполняют и выводят на печать поле ключа узла
(идентификатор). В этой программе узлы содержат только указатель
и ключ, которым служит восьмибайтовая символьная цепочка. Имена
полей — связь (link) и ключ (key).
Отметим, что хотя зависящие от данных процедуры очень корот-
кие, настоятельно рекомендуется пользоваться ими; они изолируют
части программы, которые потребуется изменять при переходе к
другому формату узла.
На рис. 9.19 показаны основная программа и процедура, которая
обрабатывает входные данные для проверки процедур связанного
списка, а на рис. 9.20 — файл данных, использованный в тестах, и
выходные данные программы.
.PSECT AVAIL
ПРОЦЕДУРА AVAIL
Эта процедура управляет пулом (списком) доступным уалов.
Она имеет три точки вмода*
INIT.AVAIL (NODES, BYTES, NODE.SIZE)
связывает все узлы в пул узлов
GET.NODE (PTR)
удаляет узел из списка доступным узлов
и возвращает его указатель.
RET_NODE (PTR)
добавляет заданный узел в список доступным узлов
209
I AVAIL содержит указатель на первый узел в связанном списке
I доступным узлов. Он инициализиУруется INIT.AVAIL и используется
| GET_NODE и RET.NODE.
I
AVAILi .BLKL 1 | Указатель первого доступного узла
I
| INIT.AVAIL (NODES, BYTES, NODE.BIZE)
I
I Входные аргументы!
I NODES пул узлов
) BYTES число Вайт в пуле
I NODE.BIZE число Вайт e узле
I
I Смещения для списка аргументов
NODES - 4
BYTES " В
NODE.SIZE " 12
I
.ENTRY INIT.AVAIL,~M<R6,RB,R9>
Использование регистров! R6 указатель текущего узла
R8 размер узла
R9 адрес предпоследнего узла
Использовать размер узла для определения адреса предпоследнего узла.
MOVL NODES(АР),R6 | Адрес первого узла
MOVL SNODE.SIZE(AP),R8 J Размер узла
ADDL3 R6,SBYTEB(AP),R9 | Адрес * число Вайт
SUBL2 RB,R9 । Адрес последнего узла
SUBL2 R8,R9 | Адрес предпоследнего /зла
5
I Установить укаоатель на первый доступный узел.
I
MOVL R6,AVAIL ; Первый уовл есть первый доступныьй
I
; Связать узлы. (Запомнить укаоатель на следующий узел во всех узлах,
I кроме последнего! запомнить пустой указатель в последнем узле.)
I
LINK! ADDL3 R6,R8,(R6) | Запомнить указатель на следующий узел
ACBL R9,R8,R6,LINK
CLRL (R6) । Запомнить пустой укаоатель в последнем
I уоле
RET
210
GET.NODE (PTR) n0 .aoi.cy .ааси :
Выходной аргумент
PTR указатель .ENTRY SET.N0DE,0 на доступный узел
MOVL AVAIL,«4(АР) BEQL DONE MOVL «AVAIL,AVAIL DONE! RET 1 1 ) RET.NODE (PTR) 1 ) Входной аргумент PTR указатель .ENTRY RET.NODE,0 ) Возвратить указатель на узел ) Сделано, если пустой ) Удалить узел из пула узла, возвращаемого в пул
l MOVL AVAIL,«4(AP) MOVL 4(AP),AVAIL RET .END ) ptr^link avail ) avail " ptr
.Р8ЕСТ INSERT
I
ПРОЦЕДУРА INSERT (KEY, FIRST)
I
I Эта процедура вводит (вставляет) новый »лемонт в связанный список.
I Предполагается, что список отсортирован. В кода условия Z в запомненном
I P8W возвращается флажок, показывающий Выло или нот место для добавления
I нового узла.
I
) АЛГОРИТМ!
I call got.nodo (now)।
; if now <> null then begin
I call fill.key (key, new))
I call find.place (key, first, ptr)j
I newslink ptr^link)
I ptr^link new)
I установить Z , показывая успех
) end
I
I
) Смещения для списка аргументов
KEY - 4
FIRST - 8
J
NEWi .BLKL 1 ) Для указателя на новый узел
^TRi .BLKL 1 ) Для указателя, возвращаемого find.plасе
211
.ENTRY INSERT,О
1 Построить узе/ i для нового >ломента<
PUSHAL NEW ’
CALLS Bl,GET_N0DE у Взять узел из пула »
TBTL NEW у Если new nul1, возврат
BEQL RET
PUSHL NEW | Адрес нового узла
PUSHL KEY(AP) | Адрес KEY
CALL #2,FILL.KEY | Заполнить поло ключа
I у Найти позицию в списке, где должен выть новый >ломент.
1 PUSHAL PTR
PUSHL FIRST(АР) у Адрес первого узла
PUSHL KEY(АР) | Адрес KEY
CALLS *3, FI ND. PLAQE,
1 1 Связать новый узел.
1 MOVL •PTR,«NEW | newslink ptrЛ1ink
MOVL NEW,«PTR | ptr*1 ink “ now
BISB2* **X04,4(FP) у Установить Z "*\уоол введен
1 RETi RET
.END
.PSECT DELETE
I
I ПРОЦЕДУРА DELETE (KEY, FIRST)
I
I Эта процедура удаляет узел с ключом KEY ио отсортированного
I связанного списка, в котором уоол-оаголооок адресуется FIRST.
I Она устанавливает вит Z в запомненном РВИ , если KEY найден
I е списке и удален. Удаленный узел возвращается, я пул узлов.
I
| АЛГОРИТМ1
у call «ind.plасе (KEY, FIRST, PTRj)
у node “ ptr^linkj'
I if node <> null then if nodo^koy KEY
I then begin
I ptr''link node'4! inky
I call ret.node(node) ।
। установить Z
I end.
I
I Смещения для списка аргументов
KEY - 4
FIRST - 8
I
PTRi .BLKL 1 I Для указателя, возвращаемого find.pl
I ENTRY DELETE,~M<R6>
I Использование регистров! R6 указатель узла
212
I Найти позиции а списка, гда можат выть KEY.
PUSHAL PTR
PUSHL FIRST(АР) 1 Адрес узла-заголовка
PUSHL KEY(AP) 1 Адрес KEY
' CALLS *83, FI ND. PLACE
MOVL QPTR,R6 1-node ptr^link
BEQL RET 1 Возврат, если Mode “ null
Определить, есть ли Фактически KEY.
PUSHL R6 1 Адрес узла
PUSHL KEY(AP) 1 Адрес KEY
CALLS •2,COMPARE 1 Проверить, ость ли KEY в
BNEQ RET 1 Возврат, если KEY нет
Удалить узел.
MOVL (R6),SPTR 1 ptr^link nodo^link
Возвратить yoi л в список доступных узлов.
PUSHL R6 1 Адрес узла
CALLS #1,RET.NODE
Установить флажок.
BI8B2 4ИХО4,4(АР) 1 Установить Z об удалении
списка
RET
END
RETi
.PSECT FIND
| ПРОЦЕДУРА FIND (KEY, FIRST, PTR)
I Эта процедура возвращает указатель (в PTR) уола, содержащего
I клан KEY. FIRST является указателем узла-заголовка списка.
I Если KEY в списке мет, возвращаемый указатель будет пустым.
I Алгоритм!
I call find.placo (KEY,FIRST, PREV)|
I ptr prevailnk|
I if ptr null then return null)
I if ptr*koy <> KEY then return null)
I elae return ptr.
I Смешения для списка аргументов
KEY - 4
FIRST - 8
PTR - 12
I
PREV! .BLKL 1 I Для указателя, возвращаемого find.place
ENTRY FIND,*M<R6>
213
I Использование регистров! R6 указатель (ptr)
I ..
I Найти позицию, где может выть KEY.
I PUSHAL PREV 1 Для указателя, возвращаемого find.pl
PUSHL FIRST(AP) 1 Адрес узла-заголовка
PUSHL KEY(AP) 1 Адрес KEY
CALLS #3,FIND_PLACE
i 1 Определить, есть ли фактически KEY,
MOVL •PREV,R6 1 ptr prev*link
BEQL NODE 1 Возврат ptr, если null
PUSHL R6 , 1 Адрес узла
PUSHL KEY(AP) 1 Адрес KEY
CALLS •2,COMPARE 1 Сравнить ключи
BEQL NODE
CLRL R6 1 Пустой (nul1)
1 1 Возврат указат ВЛЯ
NODE! MOVL R6,8PTR(AP) 1 Возврат ptr или null
RET
.END
— «a****—*——*—*.
.PSECT FIND.PLACE
ПРОЦЕДУРА FIND.PLACE (KEY, FIRST, PTR)
Эта процедура находит в отсортированном связанном списке, адресуемом
FIRST, масто, в котором должен находиться узел с клином KEY. Она
возвращает в PTR указатель узла, который предшествует узлу,
содержащему KEY.
Алгоритм!
ptr first)
.next ptr*linkj
while next <> null do
begin
if KEY <- next*key then exitloop)
ptr next|
next ptr*link'|
end)
return ptr.
I Смешения для списка аргументов
KEY - 4
FIRST - 8
PTR - 12
I
.ENTRY FIND.PLACE,*M<R6,R7>
I
I Использование регистров! R6 ptr (указатель)
I R7 next (следующий)
MOVL » FIRST(AP),R6 1 ptr ® first
NEXT! MOVL (R6),R7 1
BEQL FOUND 1 Если next я null, выполнено
PUSHL R7 1 Адрес узла
PUSHL KEYCAP) 1 Адрес ключа
CALLS #2,COMPARE 1 Сравнить ключи
BLEQ FOUND 1 KEY <я ключа узла
1 MOVL BRB R7,R6 NEXT 1 ptr next
FOUND! 1 MOVL RET R6,BPTR(AP) j Возврат ptr
• END
•PSECT PRINTLIST
I
I ПРОЦЕДУРА PRINTLIST (TITLE, FIRST)
I
1 Эта процедура печатает элементы связанного списка,
I адресуемого FIRST. Предполагается, что список имеет узел-заголовок,
j TITLE - это символьная цепочка, которая печатается как оаголовок
I списка.
I
I Смещения для списка аргументов
TITLE - 4
FIRST - 8
I
•ENTRY PRINTLIST, ~M<R6>
/
I
l Использование регистров! R6 указатель текущего узла
I
PRINTCHRS «TITLE(AP) 1 Напечатать оаголовок
MOVL FIRST(AP),R6 1 Указатель узла-заголовка
NEXT! MOVL (R6),R6 1 Следующий указатель
BEQL DONE
PUSHL R6 1 Включить в стек указатель
CALLS #1,PRINTKEY
BRB NEXT
DONE! RET
.END
Рис. 9.17. Процедуры связанного списка, не зависящие от данных. Не
рассмотренная пока команда BISB2 устанавливает бит в состояние 1
.PSECT FILL KEY
| ПРОЦЕДУРА FILL J® *4 KEY, NODE )
$ Эта процедура помещает клич KEY в поле ключа NODE.
?
I Число байт KEY.SIZE в ключе и смещение KEY.FIELD поля ключа
I от начала узла являются константами, определяемыми в данной
I процедуре.
KEY.SIZE " 8 I Число байт в ключв
KEY.FIELD “ 4 I Смещение поля ключа
I
I Смещения для списка аргументов
KEY = 4
NODE - 8
I
.ENTRY FILL.KEY,*M<R2,R3,R4,RS,R6>
I
I Использование регистров! R6 адрес узла
I RO-RS использует команда M0VC3
I
MOVL N0DE(AP),R6 । Адрес узла
M0VC3 «KEY.SIZE,«KEYCAP)«KEY.FIELD(R6) f Запомнить KEY
RET
.END
.PSECT COMPARE
I
| ПРОЦЕДУРА COMPARE (KEY, PTR)
I
; Эта процедура сравнивает KEY с ключом а узле, адресуемом PTR,
I и устанавливает коды условий в запомненном PSW, показывающие
I отношение между двумя ключами.
I
I Число байт KEY.SIZE в ключв и смещение KEY.FIELD поля ключа
I от начала узла являются константами, определяемыми в данной
I процедуре.
I
KEY.SIZE - 8 । Число байт в ключе
KEY.FIELD “ 4 | Смещение поля ключа
I
I Смещения для списка аргументов
KEY - 4
PTR - 8
I
.ENTRY COMPARE,*M<R2,R3,R6>
I
I Использование регистров! R6 укаоатель (ptr)
I R0-R3 использует команда СМРСЗ
I
216
MOVL PTR(AP),R6 j Адрес узла
СМРСЗ «KEY.SIZE,EKEY(AP> ,KEY.FIEL,f(^6) , .
BGTR RET " i L
BLSS SET.N
SET.Z: BI8B2 #'*'X04,4 (FP) ? Установить Z при равных ключах
BRB RET
SET.N: BISB2 #~X08,8(FP) 1 Установить N, еслу KEY < ptr^key
RET: RET
.END
.PSECT PRINTKEY
? ПРОЦЕДУРА PRINTKEY (PTR)
; Эта процедура печатает ключ узла, адресуемого указателем PTR.
?
; Число байт KEY.SIZE о ключе и смещение KEY.FIELD поля ключа
$ от начала узла являются константами, определяемыми о данной
; процедуре.
5
KEY.SIZE «8 । Число байт о ключе
KEY.FIELD «4 ; Смещение поля ключа
?
; Смещения для списка аргументов
PTR 4
I
TAB = 9
LINE: .BYTE TAB,OCKEY.8IZE1
I
.ENTRY PRINTKEY,*M<R2,R3,R4,R5,R6>
5
; Использование регистров: R6 указатель (ptr)
I R0-R5 использует команда M0VC3
?
MOVL PTR(AP),R6 ; Указатель
M0VC3 #KEY.SIZE,KEY.FIELD(R6),LINE+1
PRINTCHRS LINE,*KEY.SIZE+1 | Напечатать ключ
RET
.END
Рис. 9.18. Процедуры, зависящие от данных
217
ОСНОВНАЯ ПРОГРАММА
f Эта программа проверяет процедуры манипуляций связанным списком.
I Она вызывает XttfY’.’bsWlL* для связи всем узлов, затем вызывает
I TESTER для ввода и обработки контрольным данным и, наконец, вызывав
I PRINTLIBT для печати всем ключей из списка.
POOL - 250 1 Число байт в пуло узлов
NODE.SIZE : - 12 1 Число байт в узле
NODEBl .BLKB POOL
BYTESi .LONG POOL
NODE.SZt .LONS NODE.SIZE
1 FIRSTI .BLKL 1 1 Указатель узла-заголовка
ARBS11 .LONG 3 1 Список аргументов для INIT.AVAIL
.ADDRESS NODES,NODE.BZ
1 TAB - 9 1 Табуляция
LF - 10 1 Перевод строки
CR - 13 1 Возврат каретки
TITLEII ..ASCIZ /Список вначале > пуст/
TITLE2I .ASCII <LFXLF>/Bhmoa PRINTLIST/
1 .ASCIZ < CR X LF X LF X TAB >/ПРОДУКТЫ/< LF >
1 BEGIN LINKED
1 CALLG ARGS1,INIT.AVAIL | Связать все узлы
PUSHAL FIRST
CALLS •1,GET.NODE 1 Узел-заголовок
CLRL •FIRST 1 Указатель в заголовке пустой
1 PUSHL FIRST 1 Указатель узла-заголовка
PUSHAB TITLEI 1 Заголовок для пустого списка
CALLS •2,PRINTLIST у Напечатать пустой список 1 (для проверки PRINTLIST)
1 PUSHL FIRST
CALLS •1,TESTER 1 Обработать контрольные данные
1 PUSHL FIRST
PUSHAB TITLE2
CALLS •2,PRINTLIST 1 Напечатать список
1 EXIT
.END LINKED
218
I PSECT TESTER
<f ПРОЦЕДУРА TESTER (FIRST)
I и ' _ •
I Эта процедура обрабатывает входные данные для.процерки процедур
I манипуляций связанным списком. Она считывает записи из DATA.DAT
I в виде CODE KEY
I где CODE - односимвольный код, определяющий, что делать с элементом,
I a KEY- ключ >лемента. (Число байт в ключах можно варьировать,
I не изменяя TESTER.) TESTER печатет все обработанные записи вместе
I с коротким сообщением, показывающим результат предпринятого
I действия.
I
I
FIRST - 4 1 Смещение для списка аргументов
1 1 Коды
INS - ~A/I/ 1 Ввести
DEL - ~A/D/ 1 Удалить
FND -~A/F/ 1 Найти
1 BUFFERi .BLKB 80
BLANKSi .BYTE ЛХ2ОС8ОЗ
PTRi .BLKL 1 1 Для указателя, возвращаемого FIND
I
LF - 1Q
TAB - 9
HDSi .ASCIZ <LFXLF>/КОНТРОЛЬНЫЕ AAHHblE/<LF>
INSMSGi .ASCIZ <ТАВ>/введян/
NOROOMi .ASCIZ <ТАВ>/нет доступных узлов/
DELMSGi лASCIZ <ТАВ>/удален/
NOTINi .ASCIZ <TAB>/нет в списке/
ERRMSS1 .ASCIZ <TAB>/Плохой код!/
I
I
.ENTRY TESTER,*M<R2,R3,R4,RS>
I
I Ввести и напечатать контрольную запись.
I
PRINTCHRS HDS
READl M0VC3 *80,BLANKS,BUFFER
READRCRD BUFFER
PRINTCHRS BUFFER,RO
I
I Определить выполняемую операцию и вызвать соответствующую процедуру.
I
СМРВ BUFFER,BINS
BNEQ TRY.DEL
PUSHL FIRST(AP) ) Вызвать INSERT
PUSHAB BUFFER+2
CALLS *2,INSERT
BEQL IN
PRINTCHRS NORTON
BRW DONE
219
INI PRINTCHRS INSMSG
BRW №DONE
1 TRY_DELt CMPB BUFFER,#DEL BNEQ TRY.FND PUSHL FIRST(AP) $ Вызвать DELETE PUSHAB BUFFER+2 CALLS #2,DELETE BEQL DELETED PRINTCHRS NOTIN BRW DONE
DELETEDi PRINTCHRS DELMS6
BRW DONE
1 TRY.FNDi CMPB BUFFER,#FND BNEQ ERROR PUSHAL PTR | Вызвать FIND PUSHL FIRST(AP) PUSHAB BUFFER+2 CALLS #3,FIND TSTL PTR BEQL NOTFND PUSHL PTR CALLS #1,PRINTKEY BRW DONE
NOTFNDi PRINTCHRS NOTIN
BRW DONE
1 ERRORi PRINTCHRS ERRMSS
DONEi BRW READ
1 EOF t RET .ЁИВ
Рис. 9.19. Основная программа и процедура TESTER
220
Файл данных б) Выход программы I SHRIMP
I APPLES (показан двух столбцах) вводом
I SPINACH Список «начал* пуст * I BAfcON
I CHOCOLATE вводом
D PIZZA КОНТРОЛЬНЫЕ ДАННЫЕ I CHEESE
I BROWNIES вводом
I ICE CREAM I APPLES I PIZZA
J COOKIES ОДОН вводом
D SPINACH I SPINACH I ABALONE
F SPINACH ВВОДОМ вводом
I SPINACH I CHOCOLATE I DOUGHNUTS
F SPINACH вводом вводом
F ICE CREAM D PIZZA F ALMONDS
D APPLES нот в списке ALMONDS
I EBBS I BROWNIES F YAMS
I MOD SHU PORK вводом YAMS
D ALMONDS I ICE CREAM I CROISSANTS
I BANANAS вводом нот доступных узлов
I CHEESECAKE 1 COOKIES I PASTRY
F COOKIES Плохой код! мот доступных узлов
F BROWNIES D SPINACH D SPINACH
I ALMONDS удалом удален
D ZUCCHINI F SPINACH D COFFEE
I MILK нот в списке удален
I COFFE I SPINACH I WINJE
I TURNKEY вводом введен
I YAMS F SPINACH I CROISSANTS
I SHRIMP SPINACH введен
I BACON F ICE CREAM I CANTALOUPE
I CHEESE ICE CREA мет доступных уолов
I PIZZA D APPLES I CASHEWS '
I ABALONE удалом нот доступных уолов
I DOUGHNUTS I EBBS
F ALMONDS вводом ВЫХОД PRINTLIST
F YAMS I MOO SHU PORK
I CROISSANTS вводом ПРОДУКТЫ
I PASTRY D ALMONDS
D SPINACH нот в списке ABALONE
D COFFEE I BANANAS ALMONDS
I WINE вводом BACON
I CROISSANTS I CHEESECAKE BANANAS
I CANTALOUPE вводом BROWNIES
I CASHW8 F COOKIES CHEESE
нот в списке CHEE8ECA
F.BROWNIES CHOCOLAT
BROWNIES CR0I88AN
I ALMONDS DOUGHNUT
ВВОДОМ EGGS
D ZUCCHINI ICE CREA
нот в списке MILK
I MILK MOO SHU
введен PIZZA
I COFFEE SHRIMP
введен TURKEY
I TURNKEY • WINE
вводом YAMS
I YAMS
вводом
Рис. 9.20. Файл данных и выходные данные программы
9.9. Заключение
oqiJr. i'jq *
Процедура' представляет собой независимо разрабатываемый и
ассемблируемый (или компилируемый) модуль, который после вы-
зова другой программой выполняет конкретную задачу. Применение
процедур значительно упрощает разработку, отладку и модификацию
программы.
Соглашениями о связи процедур называется набор инструкций
и положений, по которым процедуре передается управление, пере-
сылаются ей аргументы, осуществляется защита данных в исполь-
зуемых вызывающей программой регистрах и возвращается управ-
ление вызывающей программе. Соглашения о связи процедур в си-
стеме VAX ориентированы на пользовательский стек, предоставляе-
мый программе операционной системой. Запоминаемая в стеке ин-
формация образует кадр вызова. Формат кадра вызова представлен
на рис. 9.12.
Процедура несет ответственность за определение используемых
ею регистров. Исходное содержимое этих регистров включается в стек
и восстанавливается при возврате управления вызывающей про-
грамме. Информация о регистрах содержится в директиве
.ENTRY.
На рис. 9.6 показан стандартный формат списка аргументов.
Списки аргументов можно сформировать в стеке или в любой области
памяти. Наиболее часто аргументы передаются с помощью ссылки,
т. е. список аргументов содержит их адреса, как показано на
рис. 9.6. В качестве альтернативного решения в список аргументов
Таблица 9.1
Команды и директивы, используемые с процедурами
Для передачи управления
.ENTRY имя______процедуры, маска___регистров Определить имя______процедуры
как глобальное имя и как точку входа процедуры; задать
маску запоминания регистров
CALLG список_______аргументов, имя_процедуры Вызвать процедуру имя________
процедуры; списка аргументов в стеке нет
CALLS число________аргументов, имя_процедуры Вызвать процедуру имя________
процедуры; список аргументов находится в стеке
RET Осуществить возврат управления программе
Для указания списка аргументов
.ADDRESS выражения Инициализировать длинные слова в памяти на значе-
ния выражений
PUSHAx операнд Включить в стек адрес' операнда (х=В, W, L
или Q)
PUSHL операнд Включить в стек операнд
Для внутренних подпрограмм
BSBB назначение Перейти к подпрограмме (В означает смещение-
BSBW назначение байт, a W—смещение-слово)
RSB Возвратить управление программе
222
помещаются значение аргумента или адрес дескриптора. Адрес
списка аргументов передается процедуре в регистре АР.
Адрес кадра вызова передается процедуре в регистре FP. По
команде RET кадр вызова локализуется и удаляются запомненные
данные.
В табл. 9.1 приведены команды и директивы ассемблера, рас-
смотренные в настоящей главе.
9.10. Упражнения
I
Замечание: все аргументы передаются ссылкой!, если не оговорено иное.
1. Напишите процедуру для печати элементов массива символьных цепочек.
Аргументами должны быть массив и число элементов (длинное слово). Считайте,
что все цепочки содержат по 12 символов.
2. Оформите программу двоичного поиска (см. рис. 7.6) как про-
цедуру. Аргументами должны быть описанные на рис. 7.6 данные TABLE,
NUM, KEY, SIZE и INDEX.
3. Предположим, что основная программа вызывает процедуру А, которая
вызывает процедуру В в двух местах, а В в свою очередь вызывает процедуру С.
Пусть далее RETMN, RETAI, RETA2 и RETB — метки ячеек возврата (рис. 9.21).
В настоящий момент выполняется процедура С, вызванная процедурой В (по-
следняя инициирована вторым вызовом из А). Приведите диаграмму стека, по-
казывающую, какие адреса находятся в нем и в каком порядке. (Вы можете обо-
значить адреса возврата метками и игнорировать любые другие данные, которые
должны находиться в стеке.)
4. Напишите команды задания списка аргументов для процедуры двоичного
поиска из упр. 3. Укажите директивы резервирования и (или) инициализации
памяти для аргументов. Пользуйтесь любыми приемлемыми данными.
5. В этом упражнении рассматриваются возможные альтернативные форматы
кадра вызова. Для каждого варианта предполагается, что команды CALL модифи-
цированы на включение в стек данных в требуемом порядке. Цель упражнения —
определить, не будет ли потеряна какая-либо информация и не возникнут ли труд-
ности при удалении из стека включенных в него данных по команде RET.
а) Можно ли перестроить в другом порядке три длинных слова, в которых
находится старое содержимое регистров АР, FP и PC?
Основная
программа
Процедура А Процедура В
CALLA
RETMN:
CALL В
RETA1:
CALL С
RETB:
CALL В
RETA2:
Рис.
9.21. Процедуры к упр.З
223
б) Можно ли включить в стек старое содержимое регистров АР, FP и РС прежде,
чем в него будет включено содержимое регистров, определенных в маске
запоминания регистров?
в) Можно ли включить в стек длинное слово, содержащее маску запомина-
ния регистров и PSW, до включения в него содержимого запоминаемых ре-
гистров?
6. Почему по команде CALLS адрес списка аргументов запоминается в рабочей
ячейке, а не загружается сразу же в регистр АР?
7. Напишите спецификатор операнда, который можно использовать для выбор-
ки: а) адреса четвертого аргумента, б) данного, которое является третьим ар-
гументом.
8. Ниже приведены список аргументов и содержимое памяти:
АР Содержимое памяти Адрес ячейки
00000003 FFFFFF6A юзе
00001042 001003С2 1040
0000 юзе FF3A0047 1044
00001068
03104200 1068
Каково будет содержимое регистра R8 после выполнения каждой из следую-
щих команд (можно считать, что перед выполнением очередной команды регистр
R8 сброшен)? a) MOVW @8(AP),R8, б) MOVL 12(АР), R8.
9. Напишите машинный код для следующих спецификаторов операндов:
а) 4(АР), б) @12(АР), в) @ALPHA (счиФая, что ALPHA=3A и спецификатор
операнда начинается в ячейке 0Е7).
10. Предположим, что основная программа вызывает процедуру А, проце-
дура А — процедуру В, процедура В — процедуру С, а процедура С — процедуру D.
Напишите команды в процедуре D, по которым в регистр R10 должны быть за-
гружены адреса: а) списка аргументов процедуры D, б) списка аргументов процеду-
ры В, в) обработчика условия процедуры А.
11. Допустим, вы пишете программу операционной системы для обработки
особых случаев. Напишите команды, которые находят последний включенный в стек
кадр вызова с ненулевым адресом обработчика условия и загружают этот адрес
в регистр R10. (Считайте, что находящиеся в стеке кадры вызова имеют хотя бы
один ненулевой обработчик условия.)
12. В процедуру PROC входят четыре аргумента, передаваемые с помощью
ссылки (в указанном^ порядке):
TAG — целое число в формате байта;
STR1NG1 — символьная цепочка;
STR1NG2 — еще одна символьная цепочка;
MSGLOC — длинное слово для результата.
Если байт TAG отрицателен, PROC передает в MSGLOC адрес STRING 1,
а если не отрицателен — адрес STR1NG2. Напишите процедуру PROC.
13. Напишите процедуру-функцию для нахождения, пробела в символьной це-
почке и возврата его адреса. Если пробелов в цепочке нет, функция должна воз-
вратить адрес байта, находящегося за последним байтом цепочки. Список аргумен-
тов содержит адреса первого и последнего байтов цепочки. (Напомним, что про-
цедура-функция возвращает свой результат в регистре R0.)
14. Напишите команды, необходимые для вызова процедуры PROC, не имею-
щей аргументов. Воспользуйтесь командой CALLS, а затем командой CALLG.
Какая из них предпочтительнее и почему?
15. Напишите процедуру для алгоритма пузырьковой сортировки из упр. 31 гл. 7.
18. Предположим, что тестируются 50 человек, которым предложено по 20 во-
просов. Ответы («истина» или «ложь») каждого из них закодированы в 20 правых
битах длинного слова и все эти длинные слова находятся в массиве ANSWERS.
Длинное слово KEY содержит правильные ответы. Результаты тестирования оцени-
ваются с помощью процедуры SCORE. Ее аргументами (по порядку) являются:
ответы тестируемого, ключ и байт, в котором SCORE возвращает число правильных
224
ответов. Предположим далее, что зарезервирована область памяти для масёива
GRADES байтов. Требуется поместить в каждый элемент GRADES число правиль-
ных ответов того человека, ответы которого находятся в соответствующем элементе
ANSWERS. Напишите команды задания списка аргументов и вызова процедуры
SCORE столько раз, сколько требуется для оценки всех результатов тестиро-
вания.
17. Напишите процедуру GETNUM со следующими аргументами.
Входные аргументы:
START — первый байт символьной цепочки;
END — байт, который находится после цепочки.
Выходные аргументы:
NUMBER — дополнительный код (длинное слово) первого найденного в це-
почке числа, представленного в символьном коде;
NEWSTART — адрес байта после первого найденного в цепочке числа, пред-
ставленного в символьном коде.
(Как обычно, список аргументов содержит адреса приведенных выше аргумен-
тов.) Процедура GETNUM должна возвратить единицу в R0, если число в сим-
вольном коде обнаружено в символьной цепочке и правильно преобразовано; в
противном случае в R0 возвращается нуль (например, если число не найдено или
если найденное число переполняет длинное слово). В последнем варианте NUMBER
и NEWSTART запоминать не нужно.
18. Напишите команды для задания списка аргументов с такими же аргу-
ментами, как в примере 9.5, но второй и третий аргументы должны передаваться
непосредственными значениями.
19. Пользуясь структурой данных и общими соглашениями из примера, при-
веденного в разд. 9.8, напишите процедуру FIND______ITH с тремя аргументами
FIRST, 1 и PTR, где FIRST — указатель головного узла в связанном списке, а
I — целое число, FIND__ITH возвращает в PTR указатель /-го узла в списке (или
пустой указатель, если в списке меньше, чем / узлов).
20. а) Предположим, что имеется массив с п элементами, размещенными
по порядку. Сколько элементов необходимо переслать в наихудшем случае для
введения нового элемента в нужную позицию? (Напомним, что в связанном списке
необходимо изменить всего два указателя и ни один элемент не пересылается.)
б) Предположим, что задано число / и требуется найти /-й элемент в списке.
Сравните объем операций, которые необходимо выполнить, если список находится
в массиве или в связанном списке.
21. Допустим, в программе, приведенной в разд. 9.8, ключами являются не
символьные цепочки, а целые числа в дополнительном коде. Укажите, какие
процедуры необходимо изменить и в чем заключаются эти изменения.
22. Процедура INSERT (разд. 9.8) не проверяет, находится ли в. списке вводимый
ключ. Модифицируйте INSERT с целью контроля такого условия и возврата в вы-
зывающую программу флага, показывающего, что элемент не введен, поскольку
он уже есть в списке. Отметим, что модифицированная процедура INSERT должна
возвращать флаг в трех ситуациях: элемент успешно введен, элемент не введен
из-за отсутствия свободных узлов и элемент не введен, так как он является ко-
пией.
23. Перепишите основную программу и процедуру TESTER из примера, приве-
денного в разд. 9.8, для работы с двумя связанными списками вместо одного.
Основная программа должна инициализировать головные узлы и указатели
L1ST1 и L1ST2 двух списков. Данные, считываемые процедурой TESTER, кроме кода
операции и ключа, должны иметь номер списка: 1 или 2. Процедура TESTER
должна «знать», что операция реализуется с указанным списком.
8 Зак. 821
225
Глава 10
Некоторые средства
языка Ассемблера
10.1. Программные секции
Директива .PSECT и ее атрибуты. Ассемблерную программу
можно разделить на несколько программных секций, которым про-
граммист присваивает имена и различные атрибуты. Организация
программных секций имеет два преимущества — обеспечиваются за-
щита команд и только'считываемых данных от случайной модифика-
ции при записи и доступ к одним и тем же данным различных мо-
дулей.
Директива .PSECT (программная секция) указывает ассембле-
ру начать обработку новой программной секции (или продолжить
обработку имеющейся). Все данные и команды, находящиеся меж-
ду нею и следующей директивой .PSECT (или директивой .END,
если директив .PSECT больше нет) будут частью этой программной
секции. Формат директивы .PSECT:
.PSECT имя программной секции, атрибуты
Имя__программной__секции может быть любым именем, удовлет-
воряющим правилам символических имен. Ниже рассматриваются
несколько (не все) атрибутов, которые могут быть определены в
директиве .PSECT.
Атрибуты WRT (записывать) и NOWRT (не записывать) пока-
зывают, разрешено ли модифицировать содержимое программной
секции во время выполнения. Атрибут NOWRT определяется для
программных секций, содержащих только считываемые данные или
команды. По умолчанию принимается атрибут WRT.
Атрибуты ЕХЕ (выполнять) и NOEXE (не выполнять) определя-
ют, можно ли содержимое программной секции выполнять как ко-
манды. По умолчанию принимается значение ЕХЕ.
Атрибуты CON (сцепить) и OVR (наложить) показывают,
считаются ли несколько директив .PSECT с одним и тем же именем
расширениями программной секции или наложениями. Атрибут
226
OVR позволяет различным модулям обращаться к одним и тем же
данным (примеры приведены ниже). По умолчанию принимается
атрибут CON.
В гл. 2 упоминалось, что эффективность выполнения команд
повышается при выравнивании данных по соответствующим грани-
цам: слова начинаются по адресам, кратным двум, длинныё слова
начинаются по адресам, кратным четырем, а тетраслова — кратным
восьми. Атрибут выравнивания программной секции определяет
максимальное выравнивание, которое достигается в программной
секции с помощью директивы .ALIGN (см. далее). Программная
секция сама выравнивается так, чтобы начинаться на границе,
указанной в ее атрибуте выравнивания. Выравнивание определяется
ключевыми словами BYTE, WORD, LONG, QUAD и PACE. (Грани-
цей страницы PAGE служит адрес, кратный 512.) По умолчанию
принимается атрибут BYTE. Когда ассемблер встречает директи-
ву резервирования памяти или инициализации, он резервирует
память или запоминает данные, начиная со следующего свободного
байта. Ячейка этого байта определяется текущим значением счет-
чика ячеек. Директива .ALIGN указывает ассемблеру при необхо-
димости увеличить значение счетчика ячеек таким образом, чтобы
оно находилось на следующей границе запрошенного типа данных.
Директива применяется в следующем виде:
ALIGN ключевое_слово
Здесь ключевое__слово представляет собой одно из приведенных
выше ключевых слов, специфицирующих выравнивание программной
секции. Не допускается, чтобы директива .ALIGN определяла
выравнивание, превышающее максимум для программной секции.
На рис. 10'1 представлена возможная организация программы,
содержащей только считываемые данные, данные, используемые и
модифицируемые программой, и команды. Отметим, что одно из
имен программных секций совпадает с именем точки входа; такое
именование допустимо, но не обязательно.
Если при выполнении программы предпринимается попытка осу-
ществить запись в программную секцию с атрибутом NOWRT или
выполнить команды из программной секции с атрибутом NOEXE,
выдается сообщение об ошибке. Данные оказываются защищенными
и можно быстро обнаружить некоторые ошибки, возникающие из-за
неправильных адресов переходов или вызовов процедур.
Пример 10.1. Выравнивание данных. При резервировании блоков памяти
для нескольких переменных или массивов необходимо тщательно упорядочить ди-
рективы резервирования и инициализации, чтобы обеспечить и поддерживать вы-
равнивание. В рервой из приведенных ниже последовательностей переменные
RANKS и SCORES не будут находиться на границах слов и длинных слов без
последних двух директив .ALIGN. Однако, перестроив операторы во вторую после-
довательность, удается обойтись без этих директив. Отметим, что во второй
8*
227
Р8ЕСТ READ.ONLY.DATA,NOWRT,NOEXE,LONG
I Директивы инициализации данных, которые
только считываются.
I
I
.PSECT DATA,NOEXE,LONG
Директивы резервирования и инициализации
| памяти.
•ALIGN LONG | Выравнивание на границу длинного словл
ARRAYt .BLKL 50
1
I
.PSECT PROGRAM,NOWRT
BEGIN PROGRAM
Команды
ч
.EXIT
.END PROGRAM
Рис. 10.1. Организация программы из программных секций
последовательности операторы рассортированы по размеру резервируемых единиц
памяти.
.PSECT DATA,NOEXE,LONG
•ALING LONG
SCALEi .BLKF 2
FLAGSl .BYTE 7,0,1
.ALING WORD
RANKS1 .BLKW 5
.ALING LONG
SCORESi .BLKL 10
.PSECT DATA,NOEXE,LONG
228
.ALING LONG
SCALEI • BLKF 2
SCORESI • BLKL 10
RANKSi . BLKW •5
FLAGSi .BYTE 7,0,1
Обработка директив .PSECT. Здесь мы кратко обсудим, каким
образом ассемблер и редактор связей интерпретируют программные
секции. Рассмотрим прежде всего обработку ассемблером одного
исходного модуля независимо от программных секций, находящихся
в других модулях.
Ассемблер образует таблицу, содержащую имя, атрибуты, теку-
щий размер и номер, которые он назначает каждой программной
секции в ассемблируемом модуле. По умолчанию создается програм-
мная секция, называемая ’’.BLANK.”, для тех программ, в которых
нет директивы .PSECT. Эта программная секция имеет атрибуты
WRT и ЕХЕ, поэтому в ней могут находиться 'данные и команды.
Атрибутом выравнивания ее служит BYTE. Так как во всех приведен-
ных выше примерах (до гл. 9) мы не пользовались директивами
.PSECT, в них применялась программная секция ’’.BLANK.”.
В начале каждой программной секции ассемблер устанавливает
счетчик ячеек на нуль. Элемент таблицы имен для каждого имени
содержит номер программной секции, в которой определяется
имя (или производится первое обращение к имени, если оно не
определяется в модуле). Хранимые в таблице значения имен пред-
ставляют собой ячейки Относительно начала программной секции.
Физическое размещение программных секций в памяти осуществляет
редактор связей, поэтому ассемблер не может полностью построить
спецификаторы операндов в команде из одной программной секции,
которая использует имена, определяемые в другой программной
секции. Он формирует байт режима, содержащий EF для относитель-
ного режима со смещением в длинное слово, и резервирует для сме-
щения длинное слово. Правильное значение смещения заполняет
редактор связей. На рис. 10.2 показаны сброс счетчика ячеек
на нуль в начале второй программной секции и обработка ассембле-
ром операндов в относительном режиме адресации.
Ассемблер запоминает последнее значение счетчика ячеек для
каждой программной секции (размер ее равен последнему значению
счетчика ячеек), поэтому позднее, после обработки другой сек-
ции, он может добавить в исходную программную секцию дополни-
тельные данные или команды. Если ассемблер встречает директиву
' 229
0000 1 Эта программа суммирует 5 элементов
0000 2 1 массива ARRAY слов и оапоминаот ре-
0000 3 5 зультат в слово SUM.
0000 4 Использование регистров!
0000 5 1 R6 укаоатель массива
0000 6 1 R7 индекс цикла
0000 7 1 R8 сумма
ОООООООО 8 .PSECT DATAyNOEXE
FFD6 0007 005D FFDE ОООА 0000 9 ARRAY! .WORD 10,-34л93у7,-42
ОООООООС ОООА 10 SUMi .BLKW 1
ооос 11 1
ооос 12 1
ОООООООО 13 .PSECT PROG,NOWRT
/ 0000 14 BEGIN PROG
0050 15 ?
56 ОООООООО'EF ЗЕ 0050 16 MOVAW ARRAY,R6 ।Инициали-
1 оировать укаоатель
57 05 DO 0057 17 MOVL #5,R7 j Индекс
1 цикла
58 34 005А 18 CLRW R8 | Сбросить
1 для суммы
58 66 АО ОО5С 19 ADDi ADDW2 (R6),R8 | Приба-
1 вить элемент
FA 57 F5 005F 20 SOBGTR R7,ADD | Управ-
| лонио циклом
OOOOOOOA'EF 58 ВО 0062 21 MOVW R8,8UM । Запом-
1 нить сумму
0069 22 1
0069 23 EXIT
0094 24 .END PROG
Рис. 10.2. Несколько программных секций
.PSECT, имя которого уже находится в его таблице с атрибутом CON,
он ассемблирует следующие за ней операторы (до очередных ди-
ректив .PSECT или .END) таким образом, как будто физически они
размещены за последним операндом предыдущей части программной
секции. Редактор связей размещает части программной секции в
памяти друг за другом. (Атрибуты любого продолжения програм-
мной секции должны быть такими же, какими они были опреде-
лены в первый раз, но их не обязательно приводить повторно.)
Редактор связей при объединении модулей программы собирает
все части каждой программной секции в единое целое, поэтому
каждая программная секция образует в памяти единый сегмент.
230
Части одной программной секции могут находиться в нескольких
модулях; редактор связей размещает их друг за другом (если у
них нет атрибута OVR) и осуществляет необходимую коррекцию
адресных обращений.
В гл. 9 было показано, как применять процедуры и как переда-
вать аргументы в процедуры и из процедур. В силу многих причин
использование процедур и их аргументов оказывается лучшим спо-
собом, позволяющим различным модулям работать с одними и теми
же данными. Это дает возможность обеспечить гибкое управление
выбором данных, доступных каждой процедуре и модифицируемых
ею. Однако иногда объем данных, разделяемых группой процедур,
оказывается очень большим. И тогда экономится масса времени, если
не нужно формировать длинные списки аргументов; программы же
получаются более понятными, если они обращаются к данным по
именам, а не косвенно через регистр АР. Следовательно, целе-
сообразно иметь программные секции, совместимые с блоками
COMMON в языке Фортран. Любой обращающийся к данным модуль
должен содержать копию программной секции, например с именем
COMMON_____DATA, в которой находятся все директивы резервирова-
ния памяти для разделяемых данных. Программная секция должна
включать атрибут OVR, показывающий редактору связей, что
директивы резервирования памяти разделяют (оверлей) одно и то
же пространство памяти, а не резервируют для каждого модуля
дополнительное пространство. Применение такой программной сек-
ции иллюстрируется рис. 10.3.
.PSECT COMMON-DATA,OVR,NOEXE,LONG
.ALIGN LONG
Xi .BLKL 1
Yi .BLKL 1
Zi .BLKL 1
1
1 .PSECT PROC,NOWRT
I Эта процедура складывает первые два длинных слова ио программной
I секции COMMON_DATA и запоминает результат в третьем.
.ENTRY
ADDL3
RET
.END
PR0C,0
XtYfZ
231
Выход
«««
А 00000001
В FFFFFFE7
С FFFFFFE8
«««
, Рис. 10.3. Программная секция с атрибутом OVR
10.2. Термы и выражения
Операнды машинных команд и аргументы ассемблерных директив
могут определяться с помощью выражений. Мы уже пользовались
простыми выражениями вида
RECORD+1 LIST-4 NUM*S1ZE
Так как выражение может состоять из одного терма, одно имя или
одно число, по существу, является ввфажением. В общем специфи-
каторы операндов описываются одним или несколькими выражения-
ми вместе со специальными символами, обозначающими используе-
мый режим адресации (например, знак =#= обозначает литералы,
а круглые скобки — регистровый косвенный .режим). В настоящем
разделе рассматриваются некоторые общие правила образования
выражений и интерпретация последних ассемблером.
Ассемблер вычисляет выражения и, если для него в выражениях
содержится недостаточно информации, на помощь приходит редак-
тор связей. Ассемблерные выражения используются не так, как
выражения в языках высокого уровня; они применяются не для
вычислений с данными времени выполнения, а в основном для опи-
сания адресов и констант в удобной для программиста форме.
Термы. Выражения образуются путем объединения термов. В трех
приведенных выше примерах показаны наиболее часто используемые
виды термов — имена и числа. Ассемблер VAX-11 MACRO до-
пускает применение термов четырех классов: чисел, имен, значений
счетчика ячеек и некоторых видов текстовых цепочек. Терм
с предшествующим унарным оператором (например, знаком «минус»
или оператором основания системы счисления) также считается
термом. Поэтому —25 и ЛХ20 могут служить примерами термов.
232
Числа представляются в нескольких системах счисления. Если
перед числом нет оператора основания системы счисления (или
число не является частью выражения с предшествующим оператором
системы счисления), ассемблер считает основание десятичным. Ниже
приведены операторы основания системы счисления:
Оператор Основание системы счисления
* B 2 (двоичная)
* 0 10 (десятичная)
* Х 16 (шестнадцатеричная)
Л О 8 (восьмеричная)
Так, определение ^XFOl 1 эквивалентно определению 61457;
оба числа ассемблер преобразует в следующую двоичную цепочку:
00000000000000001111000000010001
Любое имя представляет собой терм независимо от того, опре-
делено ли оно системой (например, ймена регистров) или пользо-
вателем (последние определяются как метки или в операторах
прямого присваивания). Значением имени, специфицированного в
качестве метки, является, как всегда, адрес, а не содержимое
адреса; содержимое недоступно ассемблеру. Значением имени, оп-
ределенного в операторе прямого присваивания, может быть или
не быть адрес, в зависимости от того, как определено имя. Ин-
терпретация выражений, содержащих имена, более подробно рас-
сматривается в следующем разделе.
Различают два вида текстовых термов: ASCII и маска регистров.
Вид терма указывает предшествующий унарный оператор ЛА или
ЛМ.
Формат терма ASCII:
ЛА 1текст1
Длина текста составляет 1—8 символов, но не должна быть больше,
чем подразумевается в контексте его использования. Термы ASCII
часто применяются для определения литеральных или непосредствен-
ных операндов, например:
MOVL #aa/name/,line
СМРВ (R8), #ЛА/-/
В команде MOVL терм ASCII не может содержать более
четырех символов, так как тип данных операнда представлен
длинным словом. По аналогичной причине терм ASCII в команде
233
СМРВ должен содержать только один символ. Ограничителем
в символьной цепочке служит наклонная черта; в директивах .ASCII
в качестве ограничителя может фигурировать другой символ, если
наклонная черта уже есть в цепочке.
Оператор ЛМ уже упоминался в гл. 9, где с его помощью
задавалась маска запоминания регистров процедуры в ее дирек-
тиве .ENTRY. Терм маски регистров имеет следующий формат:
ЛМ <список_регистров>
В зависимости от того, где употребляется терм, вводятся
некоторые ограничения на имена, которые допускается включать
в маску регистров. Например, в маску запоминания регистров
процедуры нельзя включать RO, Rl, АР, FP, SP и РС. Как же
ассемблер вычисляет терм? Терм вычисляется как 16-бито-
вая цепочка, в которой бит установлен в единицу, если в список
включен регистр (или указание о разрешении прерывания), кото-
рому этот бит соответствует.
Таким образом, для ассемблера значение терма
aM<R8,R5,R3,R9>
равно значениям термов
*80000001100101000 или *Х0328
Программисту рекомендуется пользоваться наиболее удобной и
понятной формой.
В выражении в качестве терма можно использовать значение
счетчика ячеек, которое обозначается точкой. Рассмотрим следую-
щие операторы:
TABLE: .WORD 2, -17, 45, 29, -48, 33, 38, -21
TABLE_SIZE = .-TABLE
Когда встречается второй оператор, значением «.» является адрес
ячейки байта, находящегося за последним байтом таблицы, поэто-
му имени TABLE SIZE присваивается значение, равное числу
байтов в таблице. "(Отметим, что такое определение TABLE___SIZE
сокращает объем работы и число возможных ошибок при дальнейшем
добавлении или удалении элементов таблицы; достаточно лишь
изменить директиву .WORD.)
В спецификаторе операнда значением <.» служит адрес ячейки
первого байта данного спецификатора операнда. Рассмотрим коман-
ду
BRB . + 4
234
Значением «.» оказывается адрес байта, содержащего смеще-
ние перехода. Следующие три байта будут находиться в ячейках
.4-1, .4-2 и .4-3, т. е. переход к ячейке .4-4 означает пропуск
трех байтов после команды. Конечно, для облегчения понимания
программы и уменьшения числа ошибок обычно вводится метка ко-
манды по назначению перехода и эта метка фигурирует как опе-
ранд в команде BRB. Ситуации, когда значение счетчика ячеек
используется в спецификаторах операндов, довольно редки.
Выражения. Термы объединяются в выражения с помощью бинар-
ных операторов. Наиболее часто применяются арифметические опе-
раторы, в частности «4-» и «—». Кроме того, ассемблер VAX-11
MACRO имеет несколько операторов, осуществляющих логичес-
кие операции над двоичными представлениями значений термов.
Здесь эти операторы не рассматриваются.
Термы в выражении допускается группировать с образованием
подвыражений. Так как круглые скобки применяются в спецификато-
рах операндов для другой цели (для обозначения некоторых режи-
мов адресации, например автоинкрементного и регистрового кос-
венного), подвыражения должны заключаться в угловые скобки:
S1ZE*<NUM1 + NUM2> "X<3F-2B>
L1ST+ <3*INC> <80—LENGTH>/2
Как показывает второй пример, оператор основания системы счис-
ления применим к целому выражению, а не только к одному терму.
Некоторые виды термов, например имена регистров, в выраже-
ниях нельзя объединять с другими. Выражение R54-8 просто не
имеет смысла, поскольку значением терма R5 не является содержи-
мое регистра R5. В случае неправильного выражения ассемблер
выдает сообщение об ошибке.
При вычислении выражения ассемблер не пользуется обычной
системой приоритетов операторов, принятой в языках высокого
уровня и в математических выражениях. Все бинарные операторы
имеют одинаковые приоритеты, и операции выполняются слева на-
право. Для отмены такого порядка необходимо заключать подвыра-
жения в скобки — они будут вычисляться первыми. Если, скажем,
в четвертом примере (см. выше) опустить скобки, результирующее
значение окажется другим.
Имя не всегда должно быть определено (т. е. введено в таб-
лицу имен с присвоенным значением) до включения его в выраже-
ние. Выражения с не определенными именами вычисляет редактор
связей.
В общем ассемблер вычисляет термы и выражения как 32-битовые
значения (хотя имеются и исключения). Результат усекается,
если контекст, в котором используется выражение, требует мень-
шего числа битов.
235
10.3. Типы имен и выражений
Переместимые, абсолютные и внешние выражения. Любое выра-
жение (а следовательно, и любой терм) в модуле можно отнести к
одному из трех типов: переместимое, абсолютное или внешнее.
Как уже подчеркивалось, в качестве значения ассемблер присваи-
вает имени, используемому как метка, адрес ячейки отмеченного
байта относительно начала модуля или программной секции, в ко-
торой оно находится. Ничего лучшего ассемблер сделать не мо-
жет: он «не знает», где в памяти будет находиться программа
при выполнении. Такие имена называются переместимыми. Б общем,
выражение считается переместимым, если его значение фиксирова-
но относительно начала программной секции, в которой оно появ-
ляется, а фактический адрес ячейки, представленный выражением,
зависит от того, куда помещает редактор связей программную
секцию. Метки областей данных и метки команд представляют собой
переместимые имена, а счетчик ячеек (для всех видов рассмотрен-
ных программных секций) — переместимый терм.
Выражение называется абсолютным, если его значение — кон-
станта, не зависящая от того, где в памяти размещается содержащая
его программная секция. Все числа и текстовые термы оказываются
абсолютными термами. Например, значением терма ASCII служит
символьный .код текста.
Определяемое в операторе прямого присваивания имя может
быть абсолютным или переместимым в зависимости от типа выра-
жения, которое определяет значение имени. Все имена, специфи-
цированные в следующих операторах,— абсолютные:
МАХ -40
SIZE - 32
TOTAL-SPACE - MAX«8IZE
Предположим, что ARRAY — метка массива, содержащего 200
байт. Имя LAST, определяемое как
LAST = ARRAY+199
оказывается переместимым, так как его значением служит послед-
ний байт массива. Однако то, что определяющее LAST выражение
включает в себя переместимое имя ARRAY, не дает еще доста-
точных оснований считать LAST переместимым. Рассмотрим сле-
дующие операторы:
TABLE .WORD 2,-17,45,29,-48,33,38,-21
TABLE_SIZE = .-TABLE
Здесь TABLE и «.» — переместимые термй. Но хотя они и исполь-
зуются в выражении, определяющем TABLE___________SIZE, цмя
TABLE____SIZE является абсолютным. Число байтов в таблице,
236
т. е. разность (или расстояние) между TABLE и «.» во втором опера-
торе, не зависит от того, где в' памяти ра1змещена программная
секция. Аналогично выражение, представляющее разность двух
переместимых имен, будет абсолютным.
Поскольку, как уже отмечалось, значением переместимого имени
служит адрес, сумму (произведение или частное) двух перемести-
мых имен вряд ли можно считать полезным выражением. Многие
ассемблеры просто воспринимают такие выражения как ошибки,
но ассемблер VAX-11 MACRO допускает их. Образуя сложные
выражения с переместимыми именами, программист должен очень
внимательно следить за тем, чтобы они имели смысл.
Имя называется внешним, если оно не определяется в ассембли-
руемом модуле. Выражение, в котором есть хотя бы одно внешнее
имя, тоже называется внешним. Наиболее удачный пример внешнего
имени — имя точки входа процедуры, фигурирующее в командах
CALLS и CALLG. Имя точки входа определяется директивой
.ENTRY в модуле с процедурой и является в этом модуле пере-
местимым. Еще один пример (из гл. .5) — макрокоманда BEGIN:
в ней имеются команды, обращающиеся к внешним именам, в ка-
честве которых используются метки блоков данных для макрокоманд
ввода-вывода.
Конечно, ассемблер не может полностью закодировать специфи-
катор операнда, описываемого внешним выражением, так как «не
знает» значения выражения. Он формирует байт EF для относитель-
ного режима и оставляет место для длинного слова смещения. Вы-
числение и загрузку требуемого смещения осуществляет редактор
связей.
В ассемблере VAX предполагается, что любое не определенное
в модуле имя специфицируется в некотором другом модуле, который
связывается с данным модулем до выполнения программы. Поэтому
он классифицирует любое не определенное имя как внешнее. Во
многих ассемблерах это не предусмотрено, и они требуют явного
объявления списка используемых в модуле внешних имен. Если
встретилось не определенное имя, не объявленное как внешнее,
большинство ассемблеров выдает сообщение об ошибке. Такие сооб-
щения помогают находить неправильные или пропущенные операто-
ры. Принятое в VAX предположение по умолчанию не позволяет
обнаруживать подобные ошибки до связывания программы (возмо-
жен даже пропуск ошибок, если, например, не определенное имя
случайно совпало с именем процедуры, используемой другим моду-
лем). Следовательно, требование обязательного объявления всех
внешних имен удобно для программиста, хотя и связано с увели-
чением объема работы. С помощью директивы
.DISABLE GLOBAL
программист заставляет ассемблер считать'ошибками все не опре-
деленные имена, которые не объявлены как внешние. Директива
237
.EXTERNAL список_имен
сообщает ассемблеру, что все находящиеся в списке имена следует
считать внешними.
В ряде ассемблеров введены ограничения на способы объедине-
ния внешних имен с другими термами. Ассемблер VAX-11 MACRO
таких ограничений не имеет, поэтому ответственность за правильный
смысл выражений несет программист.
Ассемблер фиксирует в таблице имен тип каждого встреченного
им имени. В листингах тип имени указывается буквами R (пере-
местимое) и X (внешнее) или знаком равенства перед его значением
(абсолютное).
Глобальные имена. Имя называется глобальным, если к нему
обращается не только модуль, в котором оно определяется, но и
другие модули. До тех пор пока одним из рассмотренных ниже
способов имя не превращено в глобальное, оно считается локаль-
ным для своего модуля и доступно только в этом модуле. Благода-
ря такой локализации одно и то же символическое имя можно
использовать в различных модулях, не опасаясь путаницы. Когда
редактор связей пытается заполнить адресное обращение к внешне-
му имени, он просматривает в остальных модулях только глобаль-
ные имена.
Существует несколько способов превращения имени в глобаль-
ное. Глобальной всегда считается точка входа основной программы
или процедуры, определяемая в директиве .ENTRY. Служащее
меткой имя становится глобальным, если оно заканчивается двумя
двоеточиями (::). (Метки блоков данных для макрокоманд ввода-
вывода превращены в глобальные именно этим способом.)' Имя,
определяемое в операторе прямого присваивания, становится гло-
бальным, если за ним следуют два знака равенства вместо одного.
Интерпретация переместимых выражений. Очень важно разли-
чать переместимые, абсолютные и внешние выражения. Во-первых,
ассемблер и редактор связей интерпретируют эти выражения
по-разному, и, во-вторых, в ассемблерах многих ЭВМ имеются
ограничения на способы объединения переместимых и глобальных
термов.
Один из главных моментов в работе ассемблера и редактора
связей в любой ЭВМ — трансляции в машинный код спецификаторов
операндов, содержащих переместимые выражения. При выполнении
программы машинный код должен определять фактическое место-
положение обрабатываемых данных. Выше было показано, что в
системе VAX эта проблема в большинстве случаев решается с
помощью относительного режима адресации. Спецификатор операн-
да кодируется как смешение от содержимого РС. Часто смещение
является абсолютным и его может вычислить ассемблер, так что
редактор связей к этой операции не привлекается. В ЭВМ без
такой адресации ассемблер должен закодировать спецификатор
операнда, помещая в команду значение имени или выражения отно-
238
сительно начала программной секции, т. е. то значение, которое
ассемблер может вычислить, пользуясь данными из своей таблицы
имен. Затем ассемблер должен каким-то образом отметить специ-
фикатор операнда, чтобы сообщить редактору связей о необходимос-
ти коррекции. Редактор связей, определив, где будет размещаться
каждая программная секция, добавляет к каждому отмеченному
операнду базовый адрес его программной секции.
Применение адресации со смещением позволяет сократить
некоторые действия редактора связей, но смещения фигурируют в
операндах машинных команд, а не в ассемблерных директивах.
Следовательно, редактору связей все-таки приходится кое-где кор-
ректировать вычисленные ассемблером значения выражений, при-
бавляя базовый адрес программной секции. Рассмотрим следующие
директивы задания списка аргументов процедуры:
ARGS: LONG 3
.ADDRESS LIST, COUNT, ITEM
\
Список аргументов будет использоваться процедурой -во время
выполнения. Процедура ожидает, что он содержит 32-битовые адре-
са аргументов, а не их ячейки относительно начала программной
секции, в которой они находятся. Поэтому, когда ассемблер встре-
чает директиву .ADDRESS, он вычисляет значения выражений,
пользуясь информацией из таблицы имен, и отмечает эти значения,
чтобы редактор связей скорректировал их, прибавив базовый адрес
программной секции. Ассемблер может правильно обработать дирек-
тиву .LONG, так как ее аргумент 3 является абсолютным; в этом
случае коррекция не требуется.
10.4. Ограничения на выражения
В большинстве машинных команд и ассемблерных директивах
в основном допускаются любые выражения, но ряд директив имеет
ограничения. Наиболее общие ограничения заключаются в том, что
все имеющиеся в выражении имена должны быть уже определены
(в том же самом модуле), а выражение должно быть абсолютным.
Мы здесь рассмотрим несколько директив с такими ограничениями
и покажем, что они обусловлены принципом работы ассемблера
и (или) контекстом, в котором применяется директива.
Напомним, что главная задача ассемблера при первом проходе
ассемблируемого модуля — построение таблицы имен. Ассемблер
должен суметь определить значения имен, специфицируемых в мо-
дуле (конечно, относительно начала программной секции). Следо-
вательно, он должен иметь возможность вычислить число байтов,
выделяемых каждому оператору при первой встрече.
Рассмотрим следующие директивы:
239
.BL К* число__единиц
.BYTE список_аргументов
.WORD список_аргументов
. LONG список_аргументов
.ADDRESS список_выражений
В директивах .BYTE, .WORD и .LONG элементы списка аргумен-
тов имеют формат:
значение [коэффициент_повторения]
Число резервируемых единиц памяти, запоминаемые значения,
коэффициенты повторения и адреса, запоминаемые директивой
.ADDRESS, определяются выражениями. Выражения, которые
влияют на объем используемой памяти и, следовательно, на
величину инкремента счетчика ячеек, должны быть абсолютными
и не содержать не определенных имен. Этим ограничениям, в част-
ности, должны удовлетворять число единиц и коэффициент________повто-
рения. Выражения, описывающие запоминаемые значения, не обя-
заны удовлетворять ограничениям, так как редактор связей запом-
нит эти значения позже. На выражения в директиве .ADDRESS
вообще не введены ограничения, поскольку ассемблер всегда резер-
вирует для каждого адреса длинное слово.
В операторе прямого присваивания выражение, которое описы-
вает присваиваемое имени значение, не должно содержать ни одного
не определенного имени. Если оно' все же содержит такое имя,
ассемблер при первом проходе не сможет найти значение опре-
деляемого имени. (Как показано в предыдущем разделе, выраже-
ние не обязательно должно, быть абсолютным.)
Выражение, описывающее маску запоминания регистров в дирек-
тиве .ENTRY, должно быть абсолютным и не содержать не опреде-
ленных имен. Так как для масок обычно используется оператор
ЛМ, а имена регистров и обозначения арифметических прерываний
всегда определены, указанное ограничение не связано с неудобства-
ми для программиста.
10.5. Заключение
Программу можно разделить на несколько программных секций
с различными атрибутами (некоторые из атрибутов приведены в
табл. 10.1). Это улучшает ее модульность, обеспечивает защиту
команд и только считываемых данных, позволяет нескольким моду-
лям непосредственно обращаться к одним и тем же данным.
Операнды машинных команд и аргументы ассемблерных директив
определяются с помощью выражений, которые вычисляет ассемблер
(или редактор связей) .* Термами в выражениях могут быть числа,
имена, значение счетчика ячеек, цепочки ASCH и маски регистров.
Числа считаются десятичными, если им не предшествует оператор
240
Таблица 10.1
Некоторые атрибуты программных секций
Атрибут, принимаемый по умолчанию Альтернативный вариант Смысл атрибута
WRT NOWRT Содержимое программной секции раз- решается (не разрешается) модифи- цировать
ЕХЕ NOEXE Содержимое программной секции раз- решается (не разрешается) выполнять
CON OVR Дополнительные части одной и той же программной секции сцепляются (нак- ладываются)
BYTE WORD, LONG, QUAD, PAGE (и другие значения) Ключевое слово показывает выравни- вание программной секции и макси- мальное выравнивание, которое опре- деляется директивой .ALIGN
основания системы счисления: ЛВ (двоичная), (шестнадцате-
ричная) и ЛО (восьмеричная).
Ассемблер вычисляет содержащие имена выражения, пользуясь
значениями из своей таблицы имен. В общем случае выражения
описывают адреса или константы. В них нельзя указывать вычисле-
ния над данными, которые находятся в памяти или регистрах во
время выполнения.
Выражения вычисляются слева направо; приоритеты операторов
не учитываются. Порядок вычислений можно изменять, заключая
подвыражения в угловые скобки.
Обычно выражения вычисляются как 32-битовые величины и при
необходимости усекаются до требуемого размера.
Выражение называется переместимым, если его значение фикси-
ровано относительно начала программной секции, в которой оно
появляется, но представляемый выражением адрес ячейки зависит от
того, в какую область памяти поместит редактор связей программную
секцию. Выражение называется абсолютным, если его значение не
зависит от размещения программной секции. Выражение называет-
ся внешним, если в нем содержатся какие-либо имена, .не определен-
ные в текущем модуле. Числа, цепочки ASCH и маски регистров
являются абсолютными, а имена могут быть переместимыми,
абсолютными или внешними.
Имя называется глобальным, если к нему обращается модуль,.
отличный от того, в котором оно определяется. Примерами гло-
бальных имен служат имена точек входа процедур.
В системе VAX проблема кодирования переместимых операндов
машинных команд решается с помощью относительного режима адре-
сации. Операнды кодируются как смещения от содержимого РС;
смещения обычно оказываются константами и не зависят от того,
где в памяти редактор связей размещает программу. (Если
ассемблер не может вычислить выражение при первой встрече с
241
ним, необходимое смещение позже вычисляет редактор связей.)
Аналогичный способ обработки переместимых выражений применяет-
ся во многих ЭВМ. В ряде ЭВМ требуется, чтобы редактор связей
прибавлял ко всем таким операндам базовый адрес программной
секции.
На допустимые выражения в некоторых директивах вводятся
ограничения, так как ассемблер должен суметь определить число
байтов памяти, выделяемое каждому оператору. Так, выражение в
директиве .BLKx, задающей число резервируемых единиц памяти,
должно быть абсолютным и не содержать не определенных имен.
10.6. Упражнения
1. Предположим, что значение .==828, когда ассемблер встречает приведенные
ниже операторы. Найдите значения всех определяемых имен и окончательное
значение программного счетчика.
.ALINS WORD
DELTAS .WORD 24,-18,3
INDi .BLKB 2
LABELS .ASCII /VAX-11/
TAOs .BYTE 1
.ALINS LONG
VOLUMES . BLKL 3
2. Перестройте операторы в предыдущем упражнении, чтобы сократить число
«пропадающих» байтов.
3. Найдите допустимые термы:
a) 2S6 А) -ЛХ2А
6) ЛХ123 в) ^А/ABCDE/
в) ^BlOOlllOlll ж) ЛА*25/2*
Г) ^XlOOlllOlll a) ~M<R5,R10,IV,R11>
4. МЫМИ Найдите допустимые или абсолютными: выражения и определите, являются ли оии перемести
а) ALPHA+8 (ALPHA парвместимов)
б) 4*ВЕТА (ВЕТА абсолютное)
в) R7+1
г) ЛХ24+15*хВ111
А) LINE+MARGIN (оба имени абсолютные)
а) LINE+MARGIN (LINE пороместимое, MARGIN абсолютное)
ж) LINE+MARGIN (LINE абсолютное, MARGIN переместимое)
о) LINE+MARGIN (оба имени переместимые)
5. Вычислите значения следующих выражений (десятичные или шестнадцатерич-
ные);
242
a) ^X36
г) 11*ЛВ11
в) лА/9/
А) 15*3*2
в) RECORD*12
«RECORD
RECORD 12А, а байт по 12А содержит
шестнадцатеричное число 38)
в. Предположим, что программа содержит следующие операторы и значение
= 1В4, когда ассемблер встречает их:
CODE - 36
ALPHAt .BLKW 27
BETAi .LONG 9
Для приведенных ниже выражений найдите их значения и определите, являются ли
они абсолютными или переместимыми. Объясните, в каких случаях для определения
значения не хватает информации: a) ALPHA—ВЕТА, б) CODE, в) BETA—CODE,
г) ВЕТА, д) ALPHA.
7. а) Для каждого их приведенных ниже операторов объясните, может ли ассемб-
лер полностью обработать оператор или потребуется привлечь редактор связей.
МАХ - 50
LISTi .BLKB MAX
ARGSi .LONG 1
ADDRESS LIST
CALLG
ARGS,PROCEDURE
б) Определите, являются ли имена MAX, LIST, ARGS и PROCEDURE
абсолютными, переместимыми или внешними.
Глава 11
Макрокоманды
11.1. Общие замечания
В гл. 4 мы рассмотрели три типа операторов ассемблерных прог-
рамм: машинные команды, ассемблерные директивы и макрокоман-
ды. Макрокоманда представляет собой псевдокоманду, которую
ассемблер заменяет последовательностью ассемблерных операторов,
указанных в ее определении (или макроопределении). Следователь-
но, макрокоманды позволяют наименовать часто требующуюся
последовательность команд, а затем пользоваться именем при обра-
щении к ней.
Часть ассемблера, которая обрабатывает макроопределения и
макрокоманды, называется макросредством или макропроцессором.
Макросредства имеются во всех ассемблерах больших ЭВМ. В
настоящем разделе обсуждаются общие свойства макрокоманд при-
менительно ко многим языкам Ассемблера. Конкретные же сведения
о макросредствах стандартного ассемблера VAX-H MACRO при-
водятся ниже.
Макроопределение вводится в программу программистом или
выбирается из библиотеки макрокоманд, доступной большинству
пользователей. Примерами макрокоманд, определения которых на-
ходятся в библиотеке, могут служить макрокоманды ввода-вывода
(см. гл. 5).
Процесс замены макрокоманды соответствующими операторами
называется макрорасширением, а сама последовательность команд,
подставляемая ассемблером вместо макрокоманды,—расширенной
макрокомандой, или расширением макрокоманды.
Команды в макрорасширении не обязательно должны быть одни-
ми и теми же при каждом вызове макрокоманды. Наиболее
общий способ их изменений — использование в макрокоманде аргу-
ментов. При расширении макрокоманды ассемблер подставляет
вместо формальных (или фиктивных) аргументов, содержащихся в
макроопределении, фактические. Имеются и другие способы варьиро-
244
вания команд в макрорасширении; некоторые из них рассматривают-
ся в последующих разделах. Такое варьирование позволяет сде-
лать макрокоманды мощными и гибкими.
Макрокоманды определяются, обрабатываются и интерпретиру-
ются ассемблером совсем не так, как процедуры (например, изме-
нять команды в процедуре при каждом ее вызове невозможно),
однако их применение обеспечивает те же преимущества, что и
применение процедур:
1. Программа становится понятной, так как макрокоманде
присваивается имя, описывающее выполняемую операцию.
2. Уменьшается число ошибок, поскольку нет нужды записывать
аналогичные последовательности команд многократно.
3. Экономится время программиста (собственно время програм-
мирования, а иногда и время на обдумывание возможного решения
задачи, выполняемой макрокомандой).
Все эти достоинства можно проиллюстрировать на примерах
макрокоманд ввода-вывода, описанных в гл. 5. Читателю рекомен-
дуется посмотреть определения макрокоманд ввода-вывода и вызы-
ваемых им процедур (в приложении D), чтобы убедиться в том,
что конкретные машинные команды, скажем, для печати строки,
оказываются довольно трудными для восприятия, если не знать,
как осуществляется управление вводом-выводом в VAX/VMS. Ко-
нечно, команда PRINTCHRS (напечатать символы) более понятна^
чем операторы в расширенной макрокоманде.
По своим возможностям и гибкости макросредства ассемблеров
существенно различаются. Так, одни макросредства допускают
рекурсивные макрокоманды (макрокоманды, которые вызывают
сами себя), другие — не допускают. Мы не будем рассматривать
здесь все особенности макросредств системы VAX, а лишь обсудим
и покажем на примерах их наиболее общие свойства. Подробная
информация о макрокомандах приводится в руководстве VAX-11
MACRO Language Reference Manual.
Различия между макрокомандами и процедурами. Так как макро-
команда во время ассемблирования заменяется в исходной програм-
ме своим расширением, ее использование становится очевидным толь-
ко при построении объектного файла. Машинные команды и ассемб-
лерные директивы в расширенной макрокоманде ассемблируются
так, как будто программист записал их в исходной программе без
применения макрокоманды. Макрокоманды — это не принадлеж-
ность ЭВМ, а макросредство ассемблера. С другой стороны, про-
цедура представляет собой отдельный модуль, транслируемый ас-
семблером в объектный модуль и выполняемый как результат дей-
ствия команды CALL. Команды CALLS, CALLG и RET являются
машинными командами системы VAX и образуют часть ее архитекту-
ры.
Чтобы подчеркнуть различия между макрокомандами и процеду-
рами, проанализируем исходную программу и объектный файл,, а
245
Использование макрокоманды
Основная программа
Макроопределение
Данные
BEGIN...
Основная программа
Использование процедуры
Данные
CAL Lx
макрокоманда
CALLx
макрокоманда
EXIT ’
.END___________
Использование макрокоманд
Основная программа
Данные
EXIT
.END
точка входа
Основная программа
Данные
точка входа
Процедура
.ENTRY...
RET
.END
a)
Использование процедуры
Процедура
точка входа
расширенная
макрокоманда
CALLx
RET
BEGIN ...
CALLx
расширенная
макрокоманда
EXIT
(макрокоманда)
EXIT ’
(макрокоманда)
б)
Рис. 11.1. Различия между макрокомандами и процедурами:
а) исходные программы, б) объектные файлы, в) ход выполнения
также выполнение программы с макрокомандами и программы с
процедурой. На рис. 11.1,а показан общий вид исходных программ,
а на рис. 11.1,6 — общий формат объектных файлов. Отметим, что
на этом этапе макроопределение не привлекается. После того
как ассемблер использовал его для расширения макрокоманд на
фазе ассемблирования, макроопределение больше не требуется.
Вместо макрокоманд в программе теперь присутствуют расширенные
макрокоманды. Процедура же появилась всего один раз. При
выполнении программы (рис. 11.1, в) вместо макрокоманд выпол-
няются расширенные макрокоманды. Каждое макрорасширение
выполняется однократно (конечно, если макрокоманда не находится
в цикле). В программе с процедурой каждый оператор CALL вы-
246
расширенная
макрокоманда
расширенная
макрокоманда
TI.....
Использование макрокоманды
Основная программа
Данные
точка входа
Использование процедуры
EXIT ’
(макрокоманда)
Рис. 11.1 (продолжение)
зывает переход в точку входа процедуры, a RET — переход в вызы-
вающую программу. Существует всего одна копия процедуры;
один и тот же код выполняется несколько раз.
11.2. Макроопределения
и некоторые примеры
Общий формат макроопределения:
.MACRO имя макрокоманды аргументы
{тело макроопределения}
.ENDM имя_макрокоманды
В соответствии с директивой .MACRO отмечается начало макро-
определения, а также определяются имена макрокоманды и формаль-
ных аргументов. Аргументы не являются обязательными; в боль-
шинстве макрокоманд они имеются, но в макрокоманде EXIT их
нет. Имена макрокоманды и формальных аргументов могут быть
любыми допустимыми символическими именами.
Тело макроопределения содержит машинные команды, ассемблер-
ные директивы и другие макрокоманды, которые вставляются в то
место исходной программы, где появляется определенная макро-
команда. Во всех приводимых ниже примерах подразумевается имен-
но такой состав макрокоманды, но в разд. 11.6 и 11.7 показано, что
тело макрокоманды может быть и несколько сложнее.
Имена формальных аргументов разрешается употреблять во всем
теле макрокоманды. Во время макрорасширения ассемблер воспри-
нимает фактические аргументы как символьные цепочки без какой-
либо конкретной интерпретации и подставляет их вместо соответ -
247
ствующих формальных аргументов. После этого ассемблер трансли-
рует полученный оператор.
Директива .ENDM отмечает конец макроопределения.
Если макроопределение не находится в библиотеке макро-
команд, оно должно быть в каждом модуле, использующем макро-
команду, так как ассемблеру требуется определение для расшире-
ния макрокоманды при ассемблировании данного модуля. В даль-
нейшем мы будем всегда помещать макроопределения в начале
модуля сразу за директивой .PSECT.
Когда ассемблер встречает макроопределение, он просто запи-
сывает имя макрокоманды в таблицу и запоминает тело макро-
команды для последующего, применения. Макроопределение необхо-
димо только в том случае, если макрокоманда появляется в програм-
ме. Обнаружив макрокоманду, ассемблер отыскивает макроопреде-
ление, чтобы найти операторы, которые нужно вместо нее подставить
в программу.
Пример 11.1. Макрокоманда CVTSL. В гл. 6 было показано, что преобразование
символьного кода в дополнительный осуществляется в два этапа. На промежуточном
этапе символьный код преобразуется в упакованный десятичный формат. Следую-
щая макрокоманда позволяет выполнить преобразование с помощью всего одной
команды:
.MACRO CVTSL NUM.DIGITS,L8N,LONG
CVTSP NUM.DIOITS,LSN,NUM.DIQITS,PKD
CVTPL NUM^DIGITS,PKD,LONG
.ENDM CVTSL
Отметим, что мы руководствовались соглашениями VAX об именовании макро-
команды и порядке определения аргументов. Формальные аргументы NUM___
DIGITS, LSN и LONG не являются символическими именами, определяемыми в
программе. (Имена формальных аргументов могут совпадать с символическими
именами, фигурирующими где-то еще в программе; ассемблер их не перепутает.)
При вызове макрокоманды осуществляется подстановка определенных в ней факти-
ческих аргументов. Если, например, в программе есть команда
CVTSL #5,RECORD+6,R8
ассемблер подставит и обработает следующие команды:
CVTSP #5,RECORD+6; #5,PKD
CVTPL #5,PKD,R8
Здесь аргумент PKD не заменяется, так как не является формальным. Для
правильного использования макрокоманды этот аргумент должен быть определен
где-то в другом месте программы.
Пример 11.2. На рис. 11.2 представлена программа с определенной в примере
.PSECT TESTCVT
I ФОРМУЛИРОВКА ЗАДАЧИ
I Эта программа считывает с терминала и оапоминает две строки данных.
I Число в первой строке сообщает, сколько чисел находится во второй
$ строке.
248
I
$ Число ио первой строки запоминается о NUM как целое о формате
I длинного слова; данные из второй строки запоминаются в массиве
; DATA длинных слов.
I
; Считается, что данные имеют следующий фиксированный формат!
I
; Первая строка! add
; Вторая строка! sdddbbsdddbb.. •sddd
?
; где* d « цифра
; । в = знак
; b ® провел
1
; Во второй строке программа считывает максимум 12 чисел.
?
?
; МАКРООПРЕДЕЛЕНИЕ
I
.MACRO CVTSL NUM.DIВITS,LSN,LONB
CVTSP NUM.DIBITS,LSN,NUM.DIBITS,PKD
CVTPL NUM.DIBITS,PKD,LONS
. ENDM CVTSL
; РЕЗЕРВИРОВАНИЕ ПАМЯТИ
NUM! .BLKL 1
DATA! .BLKL 12
PKDi .BLKB 2
BUFFER: .BLKB 80
?
1 BEBIN TESTCVT
J
; Использование регистровi R6 указатель массива
1 R7 счетчик, цикла
R8 указатель вуфера
1
READLINE BUFFER- ; Ввести число элементов
CVTSL #2,BUFFER ,R7 ; Проовразовать число элементов
CMPL R7,#12 ; Проворить, не слишком ли много
/ BLEQ STORE
MOVL #12,R7 ; Установить на 12, если слишком много
249
STORE: MOVL R7,NUM 1 Запомнить число элементов
5
MOVAL DATA,R6 Инициализировать указатель массива
MOVAB BUFFER,RS 1 Инициализировать указатель буфера
READLXNE BUFFER 1 Ввести строку данных
CVTi CVTSL #3,(RS)y(R6) + 1 Прообразовать в дополнительный код
ADDL2 #6, RS 1 Инкремент указателя буфера
SOBGTR R7,CVT 1 Управление циклом
5
1 <другая обработка>
1
EXIT
• END TESTCVT -
Рис. 11.2. Программа с макрокомандой
11.1 макрокомандой CVTSL и показано размещение макроопределения.
Пример 1L3. Макрокоманда SUM. Требуется сложить три длинных слова:
.MACRO SUM А,В,С,TOTAL
ADDL3 А,В,TOTAL
ADDL2 С,TOTAL
.ENDM SUM
Рассмотрим следующие вызовы этой макрокоманды:
a) SUM 4(R7),R9+,BETA,(R6)
б) SUM #36.R5,BETA,(R9) +
и ее расширения:
a) ADDL3 4(R7),(R9) + ,(R6)
ADDL2 BETA,(R6)
б) ADDL3 #36,Р5,(Р9) +
ADDL2 BETA,(R9) +
, В первом случае макрокоманда SUM употребляется правильно, а во втором —
неправильно. Команды сформированы верно, т. е. они не приведут к ошибкам во
время ассемблирования, но применение автоинкрементного режима для аргумента
TOTAL даст не ожидаемый эффект: первая команда ADD произведет инкремент
регистра R9, поэтому адреса получателей в двух командах ADD окажутся различ-
ными.
Пример 11.3 показывает, что для фактических аргументов мак-
рокоманды допускаются различные режимы адресации, но имеются
некоторые ограничения. Однако четко не оговорено, какие режимы
адресации разрешены для аргументов макрокоманд. Так как ассемб-
лер просто заменяет формальные аргументы фактическими, приме-
нимость конкретного режима адресации для конкретного аргумента
250
зависит от того, каким образом он используется в теле макроопре-
деления.
Пример 11.4. Макрокоманда CALL. Назначение этой макрокоманды — вызвать
процедуру. Список трех аргументов процедуры формируется в стеке.
.MACRO CALL PROC ARBI,ARG2,ARG3
PUSHAL AR03
PUSHAL ARQ2
PUSHAL ARG1
CALLS S3,PROC
.ENDM Отметим, CALL что разделителем первых двух формальных аргументов макро-
команды служит пробел, а не запятая. В качестве разделителей разрешается
употреблять пробелы и запятые. Для включения в стек адреса каждого аргумента
применяется команда PUSHAL, даже если не все аргументы представлены длинны-
ми словами. Не приведет ли это к ошибкам?
Общая макрокоманда CALL особенно удобна для ЭВМ, имеющих
более примитивную систему команд, чем система VAX. Если програм-
мист должен выписывать команды для выполнения некоторых за-
дач, которые в системе VAX автоматически реализуются командами
CALLS и CALLG, применение макрокоманды позволит сэкономить
много времени. В разд. 11.6 рассматривается более гибкая макро-
команда CALL, допускающая переменное число аргументов.
Конкатенация. Предположим, что мы хотим воспользоваться
макрокомандой SUM из примера 11.3, но иногда нам придется скла-
дывать данные, не являющиеся длинными словами. Вместо записи
различных макроопределений для каждого используемого типа
данных можно указать его как аргумент и записать определение
SUM так, чтобы специфицированный для типа данных фактичес-
кий аргумент был подставлен в середину имен формируемых команд.
Это осуществляется путем так называемой конкатенации (или
сцепления) символьных цепочек. Конкатенация означает образова-
ние из двух символьных цепочек новой цепочки. В макрокомандах
допускается конкатенация аргументов с другими символами для
создания мнемоник команд, имен операндов или любых других тре-
буемых цепочек. Место конкатенации указывается с помощью апо-
строфа.
Пример 11.5. Макрокоманда SUM с конкатенацией:
MACRO SUM
ADD*TYPE'3
ADD TYPE*2
.ENDM SUM
AyByCyTOTALyTYPE
A,B,TOTAL
CyTOTAL
Макрокоманда
SUM #36,R5,BETA,(R9),W
имеет следующее расширение:
ADDW3 #36,R5,(R9)
ADDW2 BETA,(R9)
251
Апострофы в макроопределении из примера 11.5 необходимы для
отделения имени формального аргумента от соседних символов. В
общем ассемблер заменяет формальный аргумент на фактический,
если только он находит имя формального аргумента, отделенное
' от ближайших символов разделителем (запятой, пробелом, знаком
табуляции) или каким-то другим специальным символом, например
оператором конкатенации. Если первую команду в макроопределении
записать в виде
ADDTYPE3 А,В,TOTAL
то ассемблер воспримет ADDTYPE3 как одно имя и оставит его
в команде, не заменив ничем TYPE (точно так же, как он не заменил
#36 на А в команде ADDL3 из примера 11.3). Ассемблер сформирует
оператор
ADDTYPE3 #36,R5,(R9)
а затем выдаст сообщение об ошибке, поскольку команды с мнемо-
никой ADDTYPE3 нет.
Пример 11.6. Макрокоманда резервирования памяти и инициализации. Чтобы
подчеркнуть тот факт, что операторами в теле макроопределения не обязательно
должны быть машинные команды, приведем пример макрокоманды, которая резер-
вирует память для массива и переменной, содержащей число резервируемых
элементов:
.MACRO RESERVE ARRAY,NUM,TYPE
ARRAY1 . BUT TYPE NUM
ARRAY'SIZEi • LONG NUM
. ENDM RESERVE
Эту макрокоманду можно использовать в разделе программы резервирования
памяти и инициализации следующим образом:
RESERVE TAGS,50,BYTE
Расширение макрокоманды:
TAGS: .BLKB 50
TAGSSIZE: .LONG 50
Макрорасширения в листингах программ. На рис. 11.2 представ-
лена часть файла программного листинга. Как показывает этот
пример, листинги ассемблерных программ в системе VAX обычно
содержат исходную программу в том виде, в каком ее написал прог-
раммист. Макроопределения, если они находятся в исходном моду-
ле, < а не в библиотеке макрокоманд, включаются в листинг програм-
мы. Макрокоманды приводятся в листинге без расширений. Для
252
отмены этих соглашений о листинге применяются директивы
.SHOW и .NOSHOW. Директива
.SHOW МЕВ
вызывает включение в листинг последующих макрорасширений
(точнее, всех строк, которые ассемблируются в объектный файл).
Здесь МЕВ (макрорасширение двоичное) — один из нескольких
возможных аргументов директивы .SHOW. Если ассемблер встреча-
ет директиву
.NOSHOW МЕВ
он не показывает расширений макрокоманд. Листинг с макрорасши-
рениями показан на рис. 11.3. Цифрой 1 на рисунке обозначена
директива .SHOW, цифрой 2 — место появления макрокоманды,
цифрой 3 — ее расширение (отметим отсутствие номеров строк ис-
ходной программы в строках, формируемых ассемблером), цифрой
4 — объектный код для расширенных макрокоманд и, наконец,
цифрой 5 — директива .NOSHOW, подавляющая листинг макро-
расширений до появления новой директивы .SHOW (расширение
макрокоманды READLINE не показано).
00000000 1;
оооо 2;
0000 3;
0000 4;
0000 5;
0000 6;
0000 7;
0000 8;
0000 9;
0000 10;
0000 11 ;
0000 12;
0000 13;
0000 14;
0000 15;
0000 18;
0000 17;
0000 18;
0000 19;
0000 20;
0000 21 ;
0000 22;
0000 23;
0000 24;
0000 25;
0000 28;
.PSECT TESTCVT
ФОРМУЛИРОВКА ПРОБЛЕМЫ
Эта программа вводит с терминала и запоминает
две строки данных. Число в первой строке сообщает,
сколько чисел находится во второй строке.
Число из первой строки запоминается в NUM как целое число
(длинное слово); данные из второй строки запоминается
в массиве DATA длинных слов.
Считается, что данные имеют следующий фиксированный формат:
Первая строка: add
Вторая строка: sdddbbsdddbb... sddd
где d-цифра
е-знак
Ь-пробел
Во второй строке программа считывает максимум 12 чисел.
МАКРООПРЕДЕЛЕНИЕ
Рис. 11.3. Листинг, показывающий макрорасширения
253
to
2
00000004
00000034
00000036
00000086
FF4A CF 02 FF50 CF 02
___________57 FF45 CF 02
ОС
57
FF03 CF
57
03
ОС
57
D1
15
DO
DO
56 FF03
58 FF30
CF
CF
DE
9Е
[FF1B —03 ST
I_______86 FF16 CF
58
ED
ТЯ 09]
03 36
та—
57 F5
0000
0000
0000
0000
0000
0000
0000
0000
0004
0034
0036
0086
0086
0086
00D6
00D6
00D6
00D6
00D6
00D6
00E1
00E1
00E1
00EA
00 F0
00F3
OOF 5
00F8
OOFD
OOFD
0102
0107
0107
0112
0112
0112
0119
011F
0122
0125
0125
0125
0125
0125
0155
.MACRO
CVTSP
CVTPL
.ENDM
27
28
29
30
31
32; РЕЗЕРВИРОВАНИЕ ПАМЯТИ
33;
34 NUM:
35 DATA:
36 PKD:
37 BUFFER:
38;
39;
40
41 ;
42; Использование регистров:
43
44
45
46
47
48
.BLKL
.BLKL
.BLKB
.BLKB
BEGIN
49
50
51
52 STORE:
53;
54
55
56
57
58
59 CVT:
60
61
62;
63;
64;
65
66
67
CVTSL NUM_DIGITS, LSN, LONG
NUM^DIGITS, LSN, NUM_ DIGITS, PKD
NUM-DIGITS, PKD, LONG
CVTSL
12
2
80
TESTCVT
R6
R7
R8
указатель массива
счетчик цикла
* указатель буфера
READLINE BUFFER
.SHOW
______ МЕб]
CVTSL <2, BUFFER, R7]
CVTSP #2, BUFFER, #2, PKD
iCVTPL #2, PKD, R7
---- R7, f 12
STORE
*12, R7
R7, NUM
Ввести количество чисел
Преобразовать число элементов
CM PL
BLEQ
MOVL
MOVL
MOVAL
MOVAB BUFFER, R8
.NOSHOW MEB
READLINE BUFFER
.SHOW MEB
CVTSL #3, (R8),(R6) +
CVTSP #3, (R8),#3, PKD
CVTPL >3, PKD, (R6) +
ADDL2 #6, R8
SOBGTR R7, CVT
DATA, R6
<другая обработка>
.NOSHOW MEB
EXIT
.END
TESTCVT
Рис. 11.3 (продолжение)
Проверить на максимум
; Установить на 12, если много
; Запомнить в NUM
Начальный указатель массива
Начальный указатель буфера
Ввести строку данных
Преобразовать в дополнительный код
Инкремент указателя буфера
Управление циклом
11.3. Аргументы макрокоманд
Позиционные и ключевые аргументы. Выше было показано,
что ассемблер заменяет все формальные аргументы в макроопреде-
лении фактическими, определяемыми при вызове макрокоманды.
Мы не объясняли правило замены: какой из формальных аргумен-
тов подлежит замене и каким именно фактическим аргументом он
должен быть заменен, поскольку это очевидно — первый факти-
ческий аргумент подставляется вместо первого формального аргу-
мента, второй фактический аргумент подставляется вместо второго
формального аргумента и т. д. Таким образом, роль каждого факти-
ческого аргумента зависит от его позиции в списке фактических
аргументов. Конечно, такой позиционный способ аналогичен способу
распределения ролей между операндами машинных команд и аргу-
ментами процедур. Однако некоторые макрокоманды имеют очень
большое число аргументов, поэтому запомнить точно порядок их
появления довольно трудно. Допускается записывать фактические
аргументы в любой последовательности, если ввести для каждого
из них ключевое слово, указывающее ассемблеру, какой аргумент
определяется. Ключевое слово представляет собой имя формально-
го аргумента. Формат спецификации ключевого аргумента в макро-
команде имеет вид:
имя__формального_аргумента= фактический аргумент
Пример 11.7. Использование ключевых слов. Макрокоманда RANGE определяет,
находится ли заданное целочисленное данное (DATUM) между двумя значениями
(LOW и HIGH), и инициирует переход к некоторой ячейке (BAD), если данное
оказывается вне этого диапазона:
.MACRO RANGE LOW, HIGH, DATUM, TYPE, BAD
Макрокоманду RANGE можно вызвать обычным образом, пользуясь пози-
ционными аргументами:
RANGE #0, #100,(R9),W,BADDATA
Но ее же можно вызвать с помощью ключевых аргументов, например:
RANGE DATUM=(R9),LOW= #0,HIGH= #100,BAD==BADDATA, TYPE=W
Применение ключевых аргументов снимает проблему запомина-
ния порядка следования формальных аргументов в макроопределе-
нии, однако пользователь должен знать их имена.
Если ключевые слова появляются в макрокоманде, их необходимо
использовать для каждого определяемого аргумента. /
Значения, принимаемые по умолчанию, и опущенные аргументы.
В некоторых макрокомандах могут быть один или несколько фор-
мальных аргументов, для которых почти всегда специфицируется
стандартное значение (т. е. стандартный фактический аргумент).
В макроопределении допускается указать это значение как прини-
маемое по умолчанию для данно'го аргумента. Тогда, если при вызо-
ве макрокоманды аргумент будет опушен, ассемблер подставит
значение, принятое по умолчанию. Если же при вызове макрокоман-
ды для аргумента определяется какое-то другое значение, то ассемб-
лер использует его, а значение по умолчанию игнорируется.
255
I .
Значение, принимаемое по умолчанию, специфицируется в дирек-
тиве .MACRO следующим образом (вместо записи ^рлько имени
формального аргумента):
имя_формального__аргумента = значение_по_умолчанию
Пример 11.8. Макрокоманда SUM со значением, принятым по умолчанию.
Предположим, что макрокоманда SUM (см. пример 11.5) должна применяться
в основном для сложения длинных слов и что значение формального аргумента
TYPE, принятое по умолчанию, равно L.
.MACRO SUM А,В,С,TOTAL, TYPE=L
Приведем два варианта расширения макрокоманды SUM:
SUM R5,R6,R7,XYZ SUM R5,R6,R7,XYZ,B
ADDL3 R5,R6,XYZ ADDB3 R5,R6,XYZ
ADDL2 R7tXYZ. ADDS 2 R7,XYZ
Пример 11.8. Значение, принятое по умолчанию для PRINTCHRS. Макрокоман-
да PRINTCHRS имеет значение длины, принятое по умолчанию, равное 85. Ее ди-
ректива .MACRO:
.MACRO PRINTCHRS STRING, LENGTH=#85
Отметим, что здесь перед числом 85 стоит знак #. Значение по умолчанию
должно быть представлено точно в таком виде, в каком оно будет подставляться
в команды тела макрокоманды. В макроопределении PRINTCHRS аргумент LENGTH
выглядит как операнд команды.
Аргумент в макрокоманде разрешается опускать, даже если в
макроопределении не задано значение по умолчанию. В таких си-
туациях при расширении макрокоманды ассемблер подставляет
вместо соответствующего формального аргумента пустую цепочку.
Следовательно, пустую цепочку можно считать значением, принима-
емым по умолчанию для цсех аргументов, которым в макроопреде-
лении такие значения не заданы (примеры пустых аргументов
приведены в разд. 11.6).
При использовании в макрокоманде позиционных аргументов,
когда какой-либо аргумент, кроме последнего, опускается, необходи-
мо сохранить запятую после этого аргумента. При отсутствии
запятой ассемблер подставит вместо формального аргумента,
который пользователь желает сделать пустым, следующий фактиче-
ский аргумент. (Конкретный опускаемый аргумент целесообразно
помещать в макроопределение последним, как это сдёлано в при-
мере 11.8, тогда последнюю запятую можно также опустить.)
Если в макрокоманде применяются ключевые аргументы и не-
которые из аргументов опущены, они просто удаляются; запятые
как «держатели места» не нужны, ибо порядок определения аргу-
ментов не играет роли. Таким образом, ключевые аргументы очень
удобны для макрокоманд с большим числом аргументов. Многие
ключевые аргументы имеют часто используемые значения по умол-
256
чанию. К таким макрокомандам относится и большинство системных
макрокоманд ввода-вывода.
Ключевые аргументы и аргументы, значения которых принимают-
ся по умолчанию, считаются стандартными средствами макрокоманд,
хотя способы их определения и использования в различных вычисли-
тельных системах могут варьироваться.
Специальные случаи. Если фактический аргумент макрокоманды
содержит символ, который ассемблер обычно интерпретирует как
разделитель (запятую, пробел или знак табуляции), либо точку с за-
пятой, обозначающую начало комментария, весь аргумент необходи-
мо заключить в ограничители (как правило, в угловые скобки). Пред-
положим, что макрокоманда PRINTMSG выводит на терминал свой
аргумент — символьную цепочку:
PRINTMSG <TABLE OF TRANSACTIONS>
Если не ограничить символьную цепочку «TABLE OF TRANSAC-
TIONS», то ассемблер посчитает пробелы разделителями и будет
полагать, что имеется три фактических аргумента. При наличии
в макроопределении менее трех формальных аргументов ассемблер
выдаст сообщение об ошибке.
Угловые скобки не являются частью фактического аргумента.
Ассемблер удаляет их до подстановки фактического аргумента в
операторы тела макроопределения.
В том случае, когда угловая скобка оказывается одним из
символов в фактическом аргументе, в качестве ограничителя следует
указать какой-нибудь другой символ. Допускается любой символ,
но перед первым ограничителем должен находиться диакритичес-
кий знак (Л). Например, в макрокоманде
PRINTMSG "/ITEMS WITH COST>$ 100.00/
ограничителем служит наклонная черта. Так как дйакритический
знак имеет специальный смысл, содержащий его фактический аргу-
мент необходимо заключать в ограничители. Например, правильное
определение фактического аргумента *XFF00 имеет вид
<XFF00>.
11.4. Локальные метки
Необходимость введения локальных меток. Предположим, что.
требуется макрокоманда для вычисления абсолютного значения
целого числа. Можно попробовать написать ее так, как это сделано
в следующем примере.
Пример 11.10. Макрокоманда с меткой:
.MACRO ABS SOURCE,DEST,TYPE«L
MOV TYPE SOURCE,DEST
LABEL
BGEQ
9 Зак. 821
257
MNES•TYPE DEST,DEBT
LABELi .ENDM AB8
У читателя сразу возникает несколько вопросов. Допустимо
ли отмечать оператор .ENDM? Если да, то как ассемблер интерпре-
тирует метку? Метка допустима и интерпретируется обычным обра-
зом. Помещая в программу каждую команду из макроопределения,
ассемблер транслирует ее и выполняет обычным образом инкремент
своего счетчика ячеек. Когда встречается метка, ее имя включается
в таблицу имен, а значение устанавливается равным текущему
значению счетчика ячеек. Так как в строке с меткой нет ни машинной
команды, ни директивы резервирования памяти или инициализации,
инкремент счетчика ячеек не производится. Следовательно, находя-
щийся в программе после макрокоманды оператор будет ассембли-
роваться в ячейку, адресуемую как LABEL.
Несмотря на то что употребление метки в определении ABS счи-
тается технически правильным, при повторном использовании макро-
команды в одном программном модуле возникает ошибка. Посколь-
ку LABEL не является аргументом макрокоманды, ассемблер
оставит ее в командах точно в таком виде, в каком она фигури-
рует в макроопределении. Поэтому, если ABS вызывается несколько
раз, LABEL появится более чем в одной команде и вызовет ошибку
«многократное определение метки».
Пример 11.11. Расширение ABS. Программа на рис. 11.4,а дважды вызывает
макрокоманду ABS. Ее расширения вместе с сообщениями об ошибке показаны
на рис. 11.4, б.
Используемое здесь неправильное макроопределение показано в примере 11.10
а) Операторы в программе
ABS R5, R5
ADDL2 R1, R5
ABS (R4), ALPHA, W
CMPW ALPHA, BETA
6) Расширения с сообщениями об ошибках
005C 17
005C
005F
0061
0064 LABEL:
55
55
55
11
55
DO
18
CE
ABS
MOVL
BGEQ
MNEGL
R5, R5
R5, R5
LABEL
R5, R5
%MACRO-E-SYMOUTPHAS, Имя вне фазы
0065
55 51 CO 0064 18
0067 19;
0067 20;
0067 21;
0067 22
95 AF 64 B0 0067
F7 18 006B
8E AF 90 AF AE 006D
ADDL2
R1, R5
0072 LABEL:
ABS
MOVW
BGEQ
MNEGL
(R4), ALPHA, W
(R4), ALPHA
LABEL
ALPHA, ALPHA
%MACRO-E-MULDEFLBL, Многократное определение метки
258
0072
8D AF 8B AF 81 0072 23
0077 24;
0077 25
CMPW ALPHA, BETA
.NOSHOW MEB
Рис. 11.4. Неправильное использование метки в макрокоманде
Этот пример иллюстрирует проблему, общую для всех макро-
средств: в макроопределении часто приходится использовать метки,
но обычные метки, если макрокоманда вызывается в модуле не-
однократно, применять нельзя. В макросредства включается меха-
низм генерирования меток, которые будут различаться при каждом
вызове макрокоманды. В ассемблере системы VAX такие генерируе-
мые метки называются создаваемыми локальными метками.
Использование локальных меток. Дбкальная метка имеет формат
где п — десятичное целое число в диапазоне 1^п^65 535.
Локальные метки допускается использовать и вне макрокоманд,
хотя это связано с несколькими ограничениями. Мы рассмотрим
применение локальных меток только в макрокомандах.
Ассемблер образует локальные метки, начиная с 30000$. Он
следит за тем, сколько раз создавалась метка, и при генерации
новой метки берет следующее большее число. Программист
задает локальную метку для макрокоманды, вводя имя формального
аргумента, начинающееся с вопросительного знака (например,
?LABEL), в список формальных аргументов директивы .MACRO.
При каждом вызове макрокоманды ассемблер создает новую локаль-
ную метку и подставляет ее вместо формального имени там, где
оно появляется в макроопределении.
Пример 11.12. Макрокоманда ABS с локальной меткой. Правильное макро-
определение ABS имеет следующий вид:
.MACRO ABS SOURCE,DEST,TYPE-L,7LABEL
MOV'TYPE SOURCE,DEST
BGEQ LABEL
MNEG'TYPE DEST,DEST
LABEL: .ENDM ABS
Отметим, что вопросительный знак не включается в формальное имя метки,
когда оно появляется в теле макроопределения.
Если предположить, что макрокоманды, показанные на рис. 11.4, о, оказыва-
ются в программе первыми расширяемыми макрокомандами, то их расширения
соответствуют приведенным на рис. 11.5.
MOVL
BGEG
MNEGL
300008:
ADDL2
RS,RS
300008
RS, RS
Rl^RS
расширение ABS R5,RS
9*
259
MOVW
BGEQ
MNEGW
3000111
CMPW
(R4).ALPHA
300011
ALPHA,ALPHA
ALPHA,BETA
расширение. ABS
(R4),ALPHA,W
Рис. 11.5. Расширения ABS с применением локальных меток
В макрокоманде разрешается использовать более одной локаль-
ной метки, но для каждой из них, разумеется, определяется свое
формальное имя.
Пример 11.13. Макрокоманда PRINTMSG предназначена для печати символь-
ной цепочки. Она аналогична макрокоманде PRINTCHRS, но проще в использова-
нии, так как не требует размещения цепочки в памяти. PRINTMSG записыва-
ется в виде
PRINTMSG <CLASS SCHEDULE>
(Напомним, что угловые скобки ограничивают символьную цепочку, чтобы
ассемблер не интерпретировал «CLASS» и «SCHEDULE» как два аргумента.)
Определение макрокоманды PRINTMSG:
.MACRO PRINTMSG MSG,?LBL,?LINE
BRB LBL
LINEi .ASCIZ /MSG/
LBLi PRINTCHRS LINE
•ENDM PRINTMSG
В разд. 11.1 было показано, что тело макроопределения может содержать
макрокоманду. В нашем примере макрокоманда PRINTMSG вызывает макрокоманду
PRINTCHRS.
Отметим наличие команды перехода, которая «обходит» символьную цепочку,
размещаемую в памяти между машинными командами. Опустить переход, конечно,
нельзя: это приведет к ошибке, так как без него CPU попытается выполнить
символьные данные как команды. |
Кбгда ассемблер встречает макрокоманду, он строит таблицу
замены аргументов, которой пользуется при расширении макрокоман-
ды. Предположим, что до расширения
PRINTMSG <CLASS SCHEDULE>
ассемблер уже создал в текущем модуле три локальных метки.
Тогда таблица подстановок для данного расширения принимает
вид:
Формальный аргумент Фактическое значение
№>Q CLASS SCHEDULE
LBL 30003$
LINE 30004$
260
Фактические значения подставляются вместо имен формальных
аргументов так, как они представлены в таблице, т. е. как символь-
ные цепочки. Допустим, что сразу после печати первого сообщения
макрокоманда PRINTMSG вызывается для печати второго сообще-
ния:
PRINTMSG <COURSE DAYS TIMES>
В этом случае таблица подстановок выглядит так:
Формальный аргумент Фактическое значение
MSG COURSE DAYS TIMES
LBL 30005$
LINE 30006$
Пример 11.14. Расширения макрокоманды PRINTMSG:
a) PRINTMSG <CLASS SCHEDULE>
3OOO4SI 30003*1 6) BRB •ASCIZ PRINTCHRS PRINTMSG 30003* /CLASS SCHEDULE>
30004* TIMES>
<COURSE DAYS
BRB 3000S*
30006*1 30005*1 .ASCIZ PRINTCHRS /COURSE 30006* DAYS TIME8>
Чтобы не PRINTCHRS. загромождать пример, мы не показываем здесь расширения
Читателю, вероятно, хотелось бы воспользоваться макрокомандой
типа PRINTMSG, по которой можно было бы печатать сообщения
посреди строки. В этом плане могут оказаться полезными некоторые
рекомендации, приведенные в разд. 11.7.
11.5. «Дружественные
по отношению
к пользователю» макрокоманды
Макрокомандами удобно пользоваться, если программисту не
нужно помнить массу связанных с ними подробностей: тонкостей
определения, ^ограничений, специальных требований и т. д. Макро-
команду можно применять- без опасения появления побочных
эффектов (например, разрушения данных в регистрах), если она
оказывается удобной (так как удобство означает меньшую вероят-
ность неправильного употребления) или если при ее неправильном
употреблении ошибка обнаруживается сразу же, а не после раз-
рушения данных или команд либо формирования неверных резуль-
татов, которые обнаруживаются впоследствии и найти их причину
261
оказывается намного труднее. Макроопределения можно написать
так, что они будут удобными и безопасными (в указанном выше
смысле) или не будут таковыми. Здесь мы рассмотрим некоторые
правила создания «дружественных по, отношению к пользователю»
(т. е. удобных и безопасных) макрокоманд. Эти правила особенно
важны для системных и библиотечных макрокоманд, с которыми
имеют дело многие программисты, а также для макрокоманд,
используемых одним программистом, но длительное время.
Правила создания макрокоманд. Мы уже показали несколько
способов, с помощью которых макрокоманды можно сделать более
удобными: принять для аргументов значения по умолчанию, ввести
ключевые аргументы, использовать локальные метки. Отсюда вы-
текают следующие правила создания макрокоманд:
• по возможности определять полезные значения аргументов,
принимаемые по умолчанию;
• выбирать естественные и легко запоминающиеся имена
формальных аргументов;
• пользоваться локальными метками, если метки вообще необ-
ходимы.
Макрокоманда CVTSL, приведенная в примере 11.1, представля-
ется весьма удобной, так как макроопределение удовлетворяет
стандартным соглашениям системы VAX в отношении именования
команд и порядка определения операндов. Это позволяет сформу-
лировать еще одно правило:
• . по возможности пользоваться стандартными соглашениями
языка Ассемблера (например, об именовании команд и операндов,
а также об упорядочении операндов).
Требуемая форма фактического аргумента зависит от того,
как он употребляется в макрокоманде. Иногда в соответствии с
назначением аргумента естественно ввести жесткие ограничения
на его форму. Например, фактический аргумент, который заменя-
ет ARRAY в макрокоманде RESERVE (см. пример 11.6), должен
быть символическим именем, поскольку он используется в качестве
метки. Иногда же возможно и желательно допустить разнообразие
форм. Первые три аргумента макрокоманды SUM (см. примеры
11.3 и 11.5) могут быть определены с любым режимом адреса-
ции операнда. На основании изложенного мы можем вывести
последнее правило:
• писать макроопределение таким образом, чтобы допускалось
приемлемое разнообразие форм фактических аргументов.
Как уже отмечалось, для четвертого аргумента макрокоманды
SUM нельзя применять автоинкрементный режим. В разд. 11.6 и
11.7 будет показано, каким способом можно модифицировать
определение SUM, чтобы и этот режим был разрешен.
Использование в макрокомандах регистров и рабочей памяти.
Часто для выполнения своей задачи макрокоманда должна исполь-
262
зовать некоторые регистры или некоторое рабочее пространство
в памяти. Рассмотрим макрокоманду CVTSL (см. пример 11.1)
.MACRO CVTSL NUM-DIGITS,LSN,LONG
CVTSP NUM_DIGITS,LSN,NUM_DIGITS,PKD
CVTPL NUM-DIGITS,PKD,LONG
.ENDM CVTSL
Здесь PKD — рабочее пространство памяти, необходимое для
хранения упакованного десятичного целого числа, символьный код
которого преобразуется в дополнительный. Приведенное макроопре-
деление предполагает, что пользователь определил PKD где-то
в другом месте программы и зарезервировал для него достаточную
память. Если же пользователь забыл определить PKD, редактор
связей выдаст сообщение об ошибке («не определенное имя»), и
эта ошибка будет быстро обнаружена. Однако возможна и иная
ситуация: пользователь определяет PKD, но не резервирует для
него достаточное пространство. В такой ситуации команда CVTSP
«перезапишет» любые данные, находящиеся после области PKD, что
обнаружить гораздо труднее. Следовательно, макрокоманда CVTSP
окажется удобнее и безопаснее, если она сама зарезервирует
рабочее пространство, необходимое для упакованного данного.
Но где макрокоманда может зарезервировать рабочее пространство?
Ответ очевиден — в пользовательском стеке. Для резервирования
временного рабочего пространства в стеке достаточно просто вы-
полнить декремент указателя стека на число требуемых байтов
(рис. 11.6).
Какое же число байтов нужно зарезервировать для упакованного
данного? Так как данное запоминается в длинном слове, оно должно
иметь максимум 10 разрядов, а
Рабочее
пространство
Используемое
пространство
стека
новое
значение SP
старое
значение SP
Рис. 11.6. Применение стека для резер-
вирования рабочего пространства.
SP должен всегда адресовать верх сте-
ка, так как операционная система в
любой момент времени может использо-
вать пространство выше (SP). Значение
(SP) можно использовать для адреса-
ции нового резервируемого рабочего
пространства
10-разрядное данное помещается
в шесть байтов. Однако преобра-
зуемая цепочка в символьном ко-
де может быть напечатана непра-
вильно или может быть неверно
определено NUM____DIGITS. В та-
ких случаях цепочка выйдет за
пределы 10 разрядов и, если в сте-
ке зарезервировано только шесть
байтов, остальные данные в стеке
будут перезаписаны, что приведет
к весьма запутанной ситуации.
Поскольку пользователь должен
быть застрахован от нежелатель-
ных побочных эффектов, следует
зарезервировать большее прост-
ранство. Но насколько большее?
Как найти приемлемый предел?
Команда CVTSP сама вызывает
263
ошибку зарезервированного операнда, если число разрядов, выде-
ленных для символьного кода числа или упакованного операнда,
находится вне допустимого диапазона для этих типов данных
(О—31). Значит, безопасная макрокоманда должна допускать
до 31 разряда. (Если преобразуемое целое число слишком велико
для длинного слова, по команде CVTPL устанавливается код
условия, переполнения, поэтому ответственность за контроль этой
ошибки можно возложить на пользователя.) Новое определение
CVTSL приводится в примере 11.15.
Пример 11.15. Макрокоманда с использованием стека для рабочего простран-
ства:
.MACRO CVTSL NUM_DIGITS,LSN,LONG
SUBL2 #16,SP
CVTSP NUM_DIGIT8,L8N,NUM_DIGIT8,(SP)
CVTPL NUMJDIGIT8,(SP),LONG
ADDL2 #16,SP
.ENDM CVTSL
Отметим, что макрокоманда должна возвращать указатель стека
в исходное состояние, извлекая из стека рабочие данные.
Если макрокоманда использует регистры, она должна сохранить
их первоначальное содержимое и восстановить его после завершения
выполнения операций. Здесь вновь самым подходящим местом для
временного хранения данных оказывается пользовательский стек.
Имеются две команды, позволяющие легко включать и извлекать из
стека содержимое нескольких регистров:
PUSHR маска__регистров
POPR маска регистров
Маска регистров подобна (но не идентична) маске регистров,
применяемой в директиве .ENTRY для определения запоминаемых
при вызове процедуры регистров. Это слово, в котором бит п соответ-
ствует Rn. В маске команд PUSHR и POPR допускается специфи-
цировать любой регистр (кроме РС). Маска может быть литералом
с оператором ЛМ либо находиться в памяти. (Чаще встречается
первый вариант.) Независимо от порядка указания в команде
регистров PUSHR включает содержимое всех указанных регистров
в стек так, что они будут упорядочены по своим номерам. В соот-
ветствии с командой POPR первое длинное слово копируется из
стека в регистр с наименьшим номером, указанный в ее маске, второе
длинное слово — во второй по номеру регистр и т. д. Конечно, лучше
перечислять регистры в численном порядке. Обе команды PUSHR
и POPR надлежащим образом корректируют указатель стека. Они
не модифицируют коды условий.
Обычно в команде POPR фигурирует такой же список регистров,
как и в команде PUSHR, но в принципе обе команды выполняются
независимо.
264
Пример 11.16. Выполнение команд PUSHR и POPR. Команда-
PUSHR #AM<R10,R5,R7,R0>
вызывает включение регистров в стек, как это показано на рис. 11.7, а. По команде
POPR #aM<R0,R8,R9,R5>
данные из стека передаются в регистры (рис. 11.7,6).
а) Предположим следующее содержимое регистров
R0: 00000000
R5: 00000005
R7: FFFFF3A2
R10: ОООООООА
Команда
PUSHR * aM<R10, R5, R7, R0>
включит в стек содержимое регистров следующим образом:
б) Команда
POPP * АМ < R0, R8, R9, R5>
извлечет 4 длинных слова из вершины стека, скорректирует
указатель стека и передаст данные в регистры следующим
образом:
R0: 00000000
R5: 00000005
R8: FFPFF3A2
R9: ОООООООА
Рис. 11.7. Выполнение команд PUSHR и POPR
Пример 11.17. Запоминание содержимого регистров в макрокоманде. Макро-
команда, одна из функций которой заключается в пересылке символьной цепочки
из одной области памяти в другую, может включать следующие строки:
PU8HR #^М<RO,R1,R2,R3,R4,R5 >
M0VC3 LEN,STR1,8TR2
POPR #ЛМ< RO,R1,R2,R3,R4,R5 >
Разумеется, команды PUSHR и POPR допускается использовать
и вне макрокоманд. Так, по команде PUSHR можно включить в
стек аргументы процедуры.
265
Макрокоманда CVTSL использует регистры R0—R3, но их содер-
—«имое не сохраняется, так как все команды системы VAX, по кото-
рым осуществляются взаимные преобразования символьного кода,
упакованного формата и дополнительного кода, имеют дело с этими
регистрами. Поэтому целесообразно распространить на макрокоман-
ды соглашения, принятые для аналогичных команд системы VAX.
При использовании некоторых системных макрокоманд регистры
R0 и RГ служат для возврата флагов состояния, как и в процедурах.
Поэтому старое содержимое этих регистров обычно не запоминается.
В гл. 5 отмечалось, что наши макрокоманды ввода-вывода
изменяют содержимое регистров R0 и R1.
Рассмотренные выше примеры позволяют сформулировать сле-
дующие правила для создания удобных и безопасных макрокоманд:
• макрокоманда должна сама резервировать рабочую память;
• в качестве рабочего пространства в макрокоманде по воз-
можности нужно привлекать стек; важно, чтобы указатель стека
правильно модифицировался при резервировании и освобождении
рабочего пространства памяти;
• желательно резервировать максимальный объем памяти, кото-
рый может потребоваться (в приемлемых пределах), чтобы при
появлении ошибки остальные данные в стеке (или где-то в другом
месте) не изменялись';
• при использовании в макрокоманде регистров необходимо
сохранять (в стеке) их исходное содержимое и восстанавливать
его после того, как макрокоманда заканчивает свое выполнение;
в соответствии с принятым в системе VAX соглашением содержимое
регистров R0 и R1 не сохраняется.
11.6. Условное ассемблирование
Что такое условное ассемблирование? Условное ассемблирова-
ние — это одна из возможностей макросредств, обеспечивающая им
значительную гибкость. В макроопределении задаются различные
операторы, подлежащие ассемблированию в зависимости от про-
веряемых ассемблером условий. Таким образом, ассемблеру можно
дать указание выполнить определенные вычисления, проверить
заданные условия и по полученным результатам ассемблировать
(или нет) некоторые операторы. Многие директивы условного
ассемблирования в макроопределениях как бы образуют язык прог-
раммирования, с помощью которого пользователь дает указания
ассемблеру.
Итак, кроме машинных команд, обычных ассемблерных директив
и других макрокоманд, в теле макроопределения появляются
директивы, которые управляют или воздействуют на расширение
макрокоманды. Когда ассемблер встречает машинные команды или
обычные директивы (например, директивы резервирования памяти
266
и инициализации), он помещает их в программу. Обнаружив
макрокоманду, ассемблер отыскивает определение этой макрокоман-
ды и расширяет ее. Когда же ассемблер встречает директиву
условного ассемблирования или макрорасширения, он выполняет
все предписываемые ею действия.
Для понимания и правильного использования директив условного
ассемблирования необходимо помнить, что ассемблер может прове-
рить только те условия и оперировать только теми данными, которые
существуют во время ассемблирования. Например, ассемблер может
проверять и оперировать значениями имен, но не содержимым
ячеек памяти или регистров, присутствующих в программе, так
как оно просто не существует во время ассемблирования.
Большинство выполняемых ассемблером проверок и вычислений
осуществляется над символьными цепочками, находящимися в самой
программе (например, фактические аргументы макрокоманд), а
не в памяти. С помощью условного ассемблирования можно,
скажем, проанализировать фактический аргумент макрокоманды на
представление его в правильной форме. Выше было показано,
что макрокоманда SUM (см. примеры 11.3 и .11.5) действует
неправильно, если четвертый аргумент определен в автоинкрементном
режиме. Благодаря условному ассемблированию (и средствам,
рассмотренным в разд. 11.7) появляется возможность модифици-
ровать макроопределение для проверки фактического аргумента
и указать ассемблеру те команды, которые в этом случае действуют
верно (см. упр. 21). С другой стороны, в макрокоманде CVTSL
(см. пример 11.15) полезно узнать, сколько разрядов имеет число
в символьном коде, чтобы не резервировать лишнее рабочее прос-
транство в памяти. Однако число разрядов нельзя проверить во
время ассемблирования, поскольку данные не считаны и не доступ-
ны до выполнения программы.
Если мы собираемся «программировать» ассемблер, нам необхо-
димы некоторые базовые компоненты языка программирования.
Здесь нельзя использовать команды системы VAX, так как обнаружив
эти команды в макроопределении, ассемблер просто вводит их в
исходную программу, а не выполняет. Тремя основными компонен-
тами нашего «языка программирования» для управления ассембле-
ром служат переменные, директива условного ассемблирования
(.IF) и директивы цикла (.1RP, .IRPC и .REPEAT). Мы рассмотрим
кратко и несколько других типичных для макросредств директив,
которые удобно применять в макроопределениях, поскольку под-
робное описание их применения, а также директив макропроцессора
системы VAX выходит за рамки данной книги.
Переменные. Для ассемблера определяемые пользователем имена
являются переменными. Значения переменных хранятся в таблице
имен и при необходимости ассемблер может найти их, проверить
и использовать в вычислениях. В программном модуле нельзя
изменять значения имен-меток, но значения имен, определенных
267
в операторах прямого присваивания, допускается изменять в
последующих таких операторах. При переопределении имени (т.\в.
при изменении его значения) ассемблер просто изменяет элемент
в таблице имен.
Во избежание конфликтов с применяемыми в программе име-
нами имена в макроопределении должны быть такими, чтобы прог-
раммист вряд ли мог ими воспользоваться. Имена, отличающиеся
от меток (определяемые в операторах прямого присваивания),
можно генерировать во время макрорасширения путем конкатена-
ции создаваемой локальной метки с другими символами (примеры
приведены ниже).
Директива .IF блока условного ассемблирования. Директива
.IF напоминает оператор IF в языках высокого уровня. Если
определенное в директиве .IF условие удовлетворяется, ассемблер
обрабатывает последующие операторы; в противном случае эти
операторы пропускаются. Блок условного ассемблирования имеет
следующий формат:
.IF условие аргумент(ы)
область действия
. ENDC
Таблица 11.1
Условия для директивы .IF
Условие Смысл Аргументы
полностью в краткой форме
BLANK* NOT—BLANK* IDENTICAL DIFFERENT EQUAL NOT—EQUAL GREATER LESS EQUAL LESS THAN GREATER EQUAL DEFINED NOT—DEFINED 1 Названия этих двух условий he не является ли аргумент пустой цело в нее символ пробела. В NB IDN DIF EQ NE GT LE LT GE DF NDF пускают непраЕ чкой (не содер Цепочка пустая Цепочка не пустая Аргументы иден- тичны Аргументы раз- личны Аргумент = 0 Аргумент =#() Аргумент >0 Аргумент <0 Аргумент «И) Аргумент>0 Имя определено Имя не определено шльную интерпретацию; жащей никаких символ Аргумент макро- команды Аргумент макро- команды Аргументу макро- команд, цепочки Аргументы макро- команд, цепочки Выражение Выражение Выражение Выражение Выражение Выражение Имя Имя ассемблер проверяет, ов), а не включен ли
268
Допустимые условия приведены в табл. 11.1. Аргументы в
директивах .IF представляют собой данные, проверяемые на указан-
ное условие. Они не обязательно являются аргументами макро-
команды.
Область действия условного блока может содержать любые
разрешенные в макроопределении операторы, в том числе вложенные
условнее блоки, директивы зацикливания и другие директивы,
управляющие расширением макрокоманды. Директива .ENDC
отмечает конец условного блока.
Пример 11.18. Макрокоманда CVTSL преобразования с проверкой переполнения.
Предположим, что желательно иметь вариант макрокоманды CVTSL, с помощью
которой пользователь мог бы проверить, не произошло ли в результате преобразо-
вания переполнение результата. в дополнительном коде. Если такой вариант
указан, то будут ассемблироваться команды, проверяющие переполнение и сбра-
сывающие полученное длинное слово в нуль, когда это переполнение возни-
кает. Пользователь выбирает этот вариант по четвертому аргументу. Если он
опущен, макрокоманда расширяется так, как это было показано ранее. Новое
макроопределение имеет следующий вид:
.MACRO CVTSL NUM.DIGITS,LSN,LONG,TESTOVFL,?LBL
SUBL2 #16,SP '
CVTSP NUM.DIGITS,LSN,NUM.DIGITS,(SP)
CVTPL NUM.DIIGITS,(SP)VLONG
. IF NOT.BLANK TESTOVFL
BVC LBL
CLRL LONG
.ENDC
LBLi ADDL2 #16,SP
. ENDM CVTSL
Отметим, что фактический аргумент, определяемый для TESTOVFL, не играет
существенной роли; важно лишь, пустой он или нет. Ниже показаны два расши-
рения макрокоманды:
CVTSL #3'(R8),R6 CVTSL *3'(R8),R6,0VFL
SUBL2 #16,BP 8UBL2 #16,SP
CVTSP #3'(R8) ЛЗ, (SP) CVTSP #3,(R8),#3,(SP)
CVTPL #3, (SP) VR6 CVTPL #3,(SP),R6
30003*1 ADDL2 #16,SP BVC 30003*
CLRL R6
30003*1 ADDL2 #16,SP
Метка 30003$ появляется в первом расширении, хотя проверяемое условие
было ложным и команда BVC исключена. Это объясняется тем, что отмеченный
оператор находится вне области действия блока .IF. (В примерах предполагалось,
что следующая создаваемая локальная метка есть 30003$.)
269
Подчеркнем различие между двумя условными проверками .IF
и BVC в примере 11.18. Первая из них осуществляется во время
ассемблирования, а вторая — во время выполнения. Ассемблер про-
веряет, не является ли четвертый аргумент пустым, во время
ассемблирования. Сам ассемблер условие переполнения не проверяет.
Если четвертый аргумент оказывается не пустым, ассемблер просто
вводит в программу команды BVC и CLRL, a CPU проверяет, не
возникло ли переполнение, во время выполнения программы.
Если область действия директивы .IF содержит всего один опера-
тор, допускается применять ее краткую форму. В краткой форме
она называется директивой непосредственного условного ассембли-
рования и имеет следующий формат:
.IIF условие аргумент(ы), оператор
Директива .ENDC здесь не нужна, так как не требуется отме-
чать конец условного блока. В примере 11.19 показано применение
директивы .IIF и условий IDENTICAL и DIFFERENT.
Пример 11.19. Макрокоманда CVT2S. По этой макрокоманде дополнительный
код, в котором представлено целое число, преобразуется в символьный. Операнд
в дополнительном коде может быть байтом, словом или длинным словом. Для целого
числа в форматах упакованного десятичного и длинного слова используется вре-
менное рабочее пространство в стеке. Отметим, что обращение к упакованному
данному в стеке осуществляется в режиме адресации со смещением.
.MACRO CVT28 DATUM, NUM.D18IT8, LSN ,'TYPE-L
8UBL2 #20,SP | Рабочее пространство
• IIF IDN TYPE,L, MOVL DATUM,(SP)
• IIF DIF TYPE,L, CVT'TYPE'L DATUM,(SP)
CVTLP (SP),NUM.DIGITS,4(SP)
CVTP8 NUM_DIGITS,4(8P),NUM_DI8IT8,L8N
ADDL2 #20,SP | Освободить рабочее пространство
.ENDM CVT28
Как показывает этот пример, часто желательно ассемблировать
одну последовательность операторов, если условие удовлетворяется,
и другую последовательность, если справедливо противоположное
условие. Ассемблер системы VAX позволяет определить обе альтер-
нативы с помощью директивы .IF______FALSE в условном блоке со
следующей структурой:
•IF условие аргумент(ы)
операторы, ассемблируемые, если условие истинно
.IF.FAL8E
270
операторы, ассемблируемые, если условие ложно
.ENDC
Действие условного блока с директивой .IF___FALSE аналогично
действию оператора if-then-else в языках высокого уровня.
Директива .IRP блока неопределенного повторения. Эта дирек-
тива оказывается наиболее удобной из директив циклического
условного ассемблирования. Она заставляет ассемблер многократ-
но генерировать одну и ту же последовательность операторов
с параметром, который изменяется при каждой генерации. Формат
блока неопределенного повторения:
.IRP Формальный, ар г у мент, повторения^фактические аргументы>
« область действия
. ENDR
Формальным аргументом повторения может быть любое имя,
но им не должен быть ни один из формальных аргументов макро-
команды. Он может использоваться во всей области действия блока.
Фактическими аргументами блока .IRP часто являются формальные
аргументы макрокоманды, но ими могут быть и другие цепочки.
Ассемблер по очереди подставляет каждый фактический аргумент
вместо формального аргумента повторения во всей области действия
блока. Если фактические аргументы в .IRP представляют собой
формальные аргументы макрокоманды, ассемблер подставляет
вместо них фактические аргументы макрокоманды. В результате
область действия ассемблируется для каждого из нескольких факти-
ческих аргументов макрокоманды.
Использование рассматриваемого блока показано в примере
11.20, но прежде чем привести этот пример, мы обсудим несколько
новых директив и подробнее поясним директивы управления листин-
гом.
Директива .NARG числа аргументов. При вызове некоторых
макрокоманд удобно знать число определенных фактических аргу-
ментов. Директива .NARG (число аргументов) имеет следующий
формат
.NARG имя
и устанавливает значение имени равным числу фактических аргу-
ментов.
Директивы .SHOW и .NOSHOW управления листингом. В разд.
11.2 отмечалось, что директивы .SHOW и .NOSHOW с аргументом
МЕВ можно использовать для того, чтобы показывать или не пока-
271
зывать в листинге программы операторы макрорасширений (те
операторы, которые вызывают введение кода в объектный файл).
Часто оказывается полезным, особенно при отладке макроопределе-
ний, выводить директивы блоков условного ассемблирования и пов-
торения. Если директива .SHOW определена с аргументом ME, в
листинге будет показано все макрорасширение, а не только операто-
ры, генерирующие код в объектный файл. (Другие варианты дирек-
тив .SHOW и .NOSHOW описаны в руководстве VAX-11 MACRO
Language Reference Manual.).
Пример 11.20. Макрокоманда CALL. С помощью этой макрокоманды можно
вызывать процедуру, имеющую до 10 аргументов:
.MACRO CALL PROC ARBI,ARB2,ARG3,ARG4,ARB5,ARG6,-
ARG7,ARB8,ARB9.ARB10,7L0CLBL
.XRP ARB,<ARB10,ARB9,ARBe,ARB7,ARB6,ARB5,ARB4,-
ARG3,ARG2,ARBI>
.IIF NOT,BLANK <ARG>, PUSHAB ARG
• ENDR
.NARG N'LOCLBL
CALLS ftN'LOCLBL - l.PROC
.ENDM CALL
Отметим, что формальные аргументы макрокоманды, применяемые как аргу-
менты в блоке повторения, перечислены в обратном порядке, поэтому адреса
(непустых) аргументов будут упорядочены в стеке правильно. Отметим также,
что директивы цикла повторения и условие NOT_BLANK действуют совместно,
поэтому команда PUSHAB генерируется для любого (непустого) аргумента проце-
дуры. Такой же прием используется в макрокоманде DUMPLONG (см. приложе-
ние D) с целью генерации команд печати каждого из определенных пользователем
аргументов.
Следует подчеркнуть, что в макрокоманде предусмотрена конкатенация ло-
кальной метки с буквой N, чтобы образовать имя для операторов .NARG и CALLS.
В расширениях, показанных на рис. 11.8, это имя фигурирует как N30000$. На
рисунке приняты следующие обозначения: 1 — цикл IRP с аргументами, которые
подставляются вместо формального аргумента повторения, 2 — расширение цикла
IRP, 3 — условие ложно, поэтому объектный код не генерируется, 4 — объектный
код генерируется, 5 — значение №0000$ равно трем. Формальный аргумент
повторения ARG в операторе .IIF заключен в угловые скобки, так как без них
ассемблер будет неправильно интерпретировать оператор в тех случаях, когда
ARG пустой.
Дополнительные директивы .IRPC и .REPEAT циклов. Директива
.IRPC (блок неопределенного повторения с символьным парамет-
ром) подобна директиве .1RP. Фактическими аргументами, заме-
няющими формальный аргумент блока повторения во всей области
действия .IRPC, служат отдельные символы указанной цепочки.
Формат директивы .IRPC:
.IRPC формальный_аргумент_блока__повторения, <цепочка^
В относительно простых ситуациях, когда одну и ту же последова-
тельность команд необходимо ассемблировать фиксированное число
раз, применяется директива .REPEAT со следующим форматом:
.REPEAT выражение
272
а) Показ условных директив
ОООООООО* EF 02 FB
ОМС 23 .SHOW ME
011С
011С
011С
011С
011С
011С
011С
011С
011С
011С
011С
011С
011С
011С
011С
011С
011С
011С
011С
011С
011С
011С
011С
0120
0120
лм.
2В CALL PROCESS LIST. DIM_____________________
arg, <, 7,7,7”
ИХ , DIM, LIST>
.IIF NOT_BLANK <ARG>, PUSHAB ARG
.ENDR
“"IFF-----№Y_6LANk <>, pushab-Г
Ф .IIF NOT.BLANK <>, PUSHAB
.IIF NOT.BLANK <>, PUSHAB
.IIF NOT.BLANK <>, PUSHAB
.IIF NOT.BLANK <>, PUSHAB
.IIF NOT.BLANK <>, PUSHAB
.HF NOT.BLANK <>, PUSHAB
.IIF NOT.BLANK <>, PUSHAB
.IIF NOT.BLANK <>, PUSHAB PUSHAB DIM
.IIF NOT.BLANK <LIST>,PUSHAB LIST
IS-1, PROCESS
б) Показ только строк, генерирующих код
011С 23
011С 24;
011С 26
FEEO CF OF 011С
FEEO CF OF 0120
OOOOOOOO*EF 02 FB 0124
.SHOW MEB
CALL PROCESS LIST, DIM
.IFF NOT.BLANK <DIM>, *
.IFF NOT.BLANK <LIST>
CALLS #30000$-!, PROCESS
PUSHAB DIM
PUSHAB LIST
Рис. 11.8. Расширения макрокоманды CALL
Выражение показывает, сколько раз ассемблер должен обраба-
тывать операторы в области действия блока. Оно должно быть
абсолютным и не содержать не определенных имен. .
Метки в блоках повторения. В разд. 11.5 рассматривалась
проблема употребления в макроопределениях меток: одна и та же
метка не может появиться в программном модуле у нескольких
операторов. Создаваемые ассемблером локальные метки в основном
позволяют решить эту проблему, так как при каждом вызове
макрокоманды ассемблер создает для любой формальной метки но-
вую локальную метку. Но если метка находится у оператора в
области действия блока повторения, во многих операторах расшире-
ния может появиться одна и та же метка. Рассмотрим пример
11.21: здесь макрокоманда пытается вычислить абсолютные зна-
чения четырех аргументов; расширение ее вместе с сообщениями
об ошибках показано на рис. 11.9.
Пример 11.21. Неправильное использование локальных меток:
.MACRO AB8VAL8 Al,А2,АЗ,А4,T-L,?LBL
.IRP DATUM,<А1,A2,АЗ,A4>
.IF NOT.BLANK DATUM
TST'T DATUM
273
BGEQ LBL
MNEG *T DATUM.DATUM
LBL i
.ENDC
• ENDR
.ENDM ABSVALS
0065 18
0065 19;
0065 20
0065
0065
0065
0065
0065
0065 30000$:
0065
0065
0065
54 D5 0065
11 18 0067
54 54 CE 0069
006C 30000$:
%MACRO-E-SYMOUTPHAS, Имя вне фазы
006C
006C
.SHOW ME
ABSVALS R4, R5, R6
.IRP DATUM,< R4, R5, R6>
.IF NOT—BLANK DATUM
TSTL DATUM
BGEQ 30000$
MNEGL DATUM, DATUM
.ENDC
.ENDR
.IF NOT-BLANK R4
TSTL R4 k
BGEQ 30000$
MNEGL R4, R4
006C
006C
55 D5 006C
FC 18 006C
55 55 CE 0070
0073
%MACRO-E-MULDEFIBL,
0073
.ENDC
.IF NOT-BLANK R5
TSTL R5
BGEQ 30000$
MNEGL R5, R5
30000$:
Многократное определение метки
%MACRO-E-SUMOUTPHAS,Hmr вне фазы
0073
0073 .ENDC
0073
0073
56 D5 0073
FC 18 0075
56 56 CE 0077
007A
%MACRO-E-MULDEFIBL,
Q07A
007A
007A
007A
.IF NOT-BLANK R6
TETL R6
BGEQ 30000$
.MNEGL R6, R6
30000$:
Многократное определение метки
.ENDC
Рис. 11.9. Расширения неправильной макрокоманды ABSVALS
Как же решить эту проблему? Каким образом можно повторно
генерировать аналогичную последовательность команд, содержащую
метку, но подставляя всякий раз другую метку? Одно из решений,
по крайней мере для примера с макрокомандой ABSVALS, заклю-
чается в том, чтобы применить макрокоманду ABS из примера
11.12 несколько раз. Прй каждом ее вызове будет создаваться
274
новая локальная метка. Предположим, однако, что такое решение
неприемлемо. В этом случае лучший выход из положения — на-
писать другое макроопределение, содержащее область действия бло-
ка повторения, и модифицировать последний так, чтобы в нем была
только команда, вызывающая вторую макрокоманду. Тогда локаль-
ная метка оказывается во вторрй макрокоманде и при каждом ее вы-
зове из блока повторения первой макрокоманды будет создаваться
новая метка. Предложенный способ иллюстрируется примером 11.22.
Пример 11.22. Макрокоманда ABSVALS с использованием ABS:
.MACRO ABSVALS Al,А2,АЗ,A4.T-L
.IRP DATUM,<А1,А2,АЗ,А4>
.IIF NOT.BLANK <DATUM>, ABS DATUM,DATUM,Т
ENDR
ENDM ABSVALS
Отметим, что аргументы ABS являются формальными аргументами блока пов-
торения и ABSVALS. Они будут заменяться фактическими аргументами макро-
команды ABSVALS (или по умолчанию «L» вместо Т). Ниже приведено расширение
макрокоманды ABSVALS:
.SHOW ME
ABSVALS R4,RS,R6
.IRP DATUM,<R4, R5,R6>
.IIF NOT.BLANK <DATUM>,AB8 DATUM,DATUM,L
.ENDR
.IIF NOT.BLANK <R4>, ABS R4,R4,L
MOVL R4,R4
BGEQ 30002®
MNEGL R4,R4
30002®t
.IIF NOT.BLANK <R5>, ABS R5,R5,L
MOVL R5,R5
BGEQ 30003®
MNEGL R5,R5
30003®1
.IIF NOT.BLANK <R6>, ABS R6,R6,L
MOVL R6,R6
BGEQ 30004®
MNEGL R6,R6
30004®s
Директива .ERROR индикации сообщений об ошибках. Директива
.ERROR заставляет ассемблер вывести сообщение об ошибке на
терминал и включить его в листинг программы. Она очень удобна
.для макроопределений, в которых при проверке требуемой формы
275
фактических аргументов макрокоманды применяются директивы
условного ассемблирования. Несколько упрощенный формат дирек-
тивы .ERROR имеет следующий вид:
.ERROR ; индицируемое сообщение
Отметим, что сообщение об ошибке записывается как коммента-
рий. (Такое требование языка Ассемблера системы VAX не явля-
ется общепринятым.)
Директива .MEXIT выхода из макрокоманды. Директива .MEXIT
предназначена для окончания макрорасширения до достижения ди-
рективы .ENDM или для окончания расширения цикла повторения.
Если ассемблер встречает директиву .MEXIT внутри цикла повторе-
ния, он выходит из цикла и продолжает расширение макрокоманды
с оператора< который находится после .ENDR. Если же директива
.MEXIT встречается вне цикла, ассемблер прекращает расширение
команды.
Пример 11.23. Применение директив .ERROR и .MEXIT. Предположим, тре-
буется написать макроопределение, позволяющее проверить, что тип Q (тетраслово)
не определен как фактический аргумент в макрокоманде, которая рассчитана
только на типы данных В, W и L.
Задача решается следующим образом:
.TF IDENTICAL TYPE,О
•ERROR I О - недействительный тип
.MEXIT
.ENDC
Если условие удовлетворяется (т. е. аргумент имеет тип Q), индицируется
сообщение об ошибке. Ассемблер прекращает расширение макрокоманды и про-
должает ассемблирование с оператора, который находится за расширяемой макро-
командой.
* 11.7. Функции
обработки цепочек
В ассемблере системы VAX предусмотрены три специальных
функции, которые применяются во время макрорасширения для
анализа и обработки символьных цепочек. Каждая из этих функций
формирует как свое значение число или. символьную цепочку.
Их можно использовать в любом месте макроопределений, где
оператор, образующийся при подстановке значения функции, оказы-
вается допустимым. Названия функций начинаются со знака процен-
та, что позволяет отличать их от символических имен.
Функция LENGTH. Формат функции длины имеет вид:
% LEN GTH (цепочка)
Значением функции является длина (число символов) цепочки.
Аргумент цепочка может быть аргументом макрокоманды или ограни-
276
ценной цепочкой. (Ею может быть формальный аргумент блока
повторения, который заменяется аргументом макрокоманды или дру-
гой цепочкой.) Значение, функции длины допускается присваивать
имени и проверять с помощью условного^ссемблирования.
Пример 11.24. Применение функции % LENGTH. Предположим, что по макро-
команде WORK должна обрабатываться как аргумент символьная цепочка, но
при условии, что длина цепочки не превышает, скажем, восьми символов. Следую-
щие операторы показываются, каким образом имени можно присвоить значение длины
для использования в последующих операторах:
.MACRO WORK AR0,7L0CLBL
L' L0CLBL-7.LENGTH (ARG)
.IIF GREATER.THAN L'LOCLBL - 8, L'L0CLBL«8
Функция EXTRACT. Назначение функции EXTRACT — выделить
из символьной цепочки подцепочку. Формат этой функции:
%EXTRACT (начальная__позиция, длина, цепочка)
Аргумент цепочка является той цепочкой, из которой выделяется
подцепочка. Она может быть аргументом макрокоманды или ограни-
ченной цепочкой (или формальным аргументом блока повторения,
заменяемым любой из указанных выше цепочек). Начальная позиция
и длина определяются десятичными числами или именами. Позиции
в символьной цепочке нумеруются слева направо, начиная с нуля.
Так, цепочка «CLASS SCHEDULE» содержит букву С в позициях
О и 7, букву. S в позициях 3, 4 и 6 и пробел в позиции 5.
Пример 11.25. Применение функции %EXTRACT. Значением функции
% EXTRACT (0,5, < CLASS SCHEDULER
является символьная цепочка «CLASS», а значением функции ,
% EXTRACT (8,3, < CLASS SCHEDULER
символьная цепочка <HED>.
Если функция %EXTRACT(0,L'LOCLBL,ARG) появляется в макроопределении
из примера 11.24, ее значением будет фактический аргумент, подставленный
вместо ARG, или его первые восемь символов при длине, большей восьми.
По макрокоманде DUMPLONG (см. приложение D) печатается только
восемь символов имени каждого из ее аргументов. Для выделения первых
восьми символов имени (или всего имени, если оно состоит из меньшего числа
символов) в ней применяется функция ^EXTRACT.
Функция LOCATE. Функция LOCATE отыскивает в цепочке
заданную подцепочку и возвращает ее позицию, если подцепочка
найдена. Формат функции:
% LOCATE (цель, цепочка)
% LOCATE (цель, цепочка, начальная_позиция)
Первый аргумент цель —' это отыскиваемая цепочка, а второй
аргумент — цепочка, в которой ведется поиск. Оба аргумента
могут быть аргументами макрокоманды или ограниченными цепочка-
ми (или формальными аргументами блока повторения). Если третий
аргумент (начальная нозыцая) опущен, поиск ведется с начала це-
277
почки (позиция 0). Если же начальная позиция определена, поиск
начинается в цепочке с этой позиции. Начальная позиция может
быть числом или именем. Значением, возвращаемым LOCATE, явля-
ется номер позиции, с которой цель начинается в цепочке. Если же
цель не найдена, функция формирует длину просмотренной цепочки.
(Так как номера позиций начинаются с нуля, длина равна позиции
сразу за последним символом цепочки.) Например, значеним
% LOCATE (<МА>, <GAMMA, SIGMA>)
является число 3, а значением
% LOCATE «МА>, < GAMMA, SIGMA>,4)
—число 9. Значение функции
%LOCATE(<MAA>, <GAMMA, SIGMA>)
равно 11.
Пример П.2в. Применение функции %LOCATE.
Чтобы определить, найдена или нет целевая цепочка, можно воспользоваться
следующим условным блоком:
.IF NE ’/.LOCATE(цель, цепочка) - /.LENGTH (цепочка)
4 операторы, ассемблируемые, если цель найдена
.ENDC
Предположим, например, что нужно проверить, имеет ли фактический аргу-
мент, определенный для формального аргумента TYPE, значение В, W или L. В
примере 11.23 показано, как проверить, что аргументом является Q, но не как проверить
любой другой недействительный символ. Приводимый ниже блок условного ассембли-
рования иллюстрирует полезный способ.
.IF EQ XLOCATE(TYPE,<ВWL>)-3
.ERROR I В макрокоманд» неправильный тип
MEXIT
. ENDC
11.8. Заключение
Макросредства языка Ассемблера позволяют программисту за-
менить одним оператором целую последовательность ассемблерных
операторов. Механизмы применения аргументов и условного ассемб-
лирования допускают варьирование команд при каждом вызове
макрокоманды. Как и процедуры, макрокоманды делают программу
более понятной, модульной и простой для разработки. В отличие
278
от процедур реальные команды, ассемблируемые илй выполняемые
макрокомандой, помещаются непосредственно в программу и появля-
ются в объектном файле при каждом вызове этой макрокоманды.
Фактические аргументы определяются в макрокоманде и непос-
редственно заменяют имена соответствующих формальных аргумен-
тов, фигурирующие в макроопределении. Фактические аргументы
специфицируются позиционно или с помощью ключевых слов (имен
формальных аргументов). В макроопределении допускаются значе-
ния, принятые по умолчанию, которые используются в тех случаях,
когда программист при вызове макрокоманды не определяет
значения фактических аргументов.
Возможно варьирование команд в макрокомандах посредством
конкатенации аргументов с другими цепочками из макроопределения.
Обычные метки нельзя применять в макрокомандах, так как
одна и та же метка не может появляться в пределах одного мо-
дуля в нескольких операторах. Ассемблер генерирует для макро-
команд локальные метки; при каждом расширении макрокоманды
с локальной меткой ассемблер генерирует новую метку.
Если в макрокоманде требуется зарезервировать рабочее про-
странство в памяти, как правило, это делается в пользовательском
стеке.
Макроопределения могут содержать директивы условного ассемб-
лирования. Они заставляют ассемблер проверять различные усло-
вия и ассемблировать некоторые операторы в зависимости от
полученных результатов. Ассемблеру можно дать и другие указа-
ния, например повторить ассемблирование ряда операторов, окон-
чить расширение макрокоманды, если удовлетворяется заданное
условие, и т. д.
В макропроцессоре системы VAX предусмотрены три функции
обработки цепочек, которые весьма удобно применять вместе с
директивами условного ассемблирования.
11.9. Упражнения
1. Напишите макрокоманду, по которой обмениваются два длинных слова.
2. Напишите макрокоманду, по которой обмениваются два данных. Считайте,
что типы данных одинаковы (В, W, L или Q) и сделайте тип данных аргументом.
3. Выше подчеркивалось, что последний аргумент в макрокоманде SUM (см.
пример 11.3) не может быть определен в автоинкрементном режиме. Назовите
еще два режима адресации, которые оказываются неправильными для этого аргу-
мента.
4. Напишите макроопределение макрокоманды CVTLQ, которая преобразует
свой первый аргумент из длинного слова в тетраслово и помещает результат
во второй аргумент. В кодах условия необходимо отразить знак результата. Дру-
гими словами, макрокоманда CVTLQ должна действовать так же, как и имеющиеся
команды системы VAX, вызывающие преобразование типов целочисленных данных.
Укажите, какие режимы адресации допустимы для обоих аргументов макрокоманды.
(Список допустимых режимов для второго аргумента будет очень коротким.)
5. Предположим, что требуется с помощью макрокоманды CALL (из примера
279
11.4) вызвать процедуру PROC, аргументы которой размещены следующим образом:
адрес первого аргумента находится в регистре R7, второй аргумент находится
в ячейке памяти с меткой ALPHA; а третьим аргументом является массив
ARRAY. Напишите макрокоманду CALL для вызова процедуры.
6. Какие режимы адресации вызовут ошибки при использовании их для
фактических аргументов макрокоманды CALL из примера 11.4? Поясните, почему
это произойдет.
7. Может ли второй аргумент в макрокоманде RESERVE из примера 11.6 быть
именем? Ответ поясните.
8. Напишите определение макрокоманды PUSH, по которой в стек включаются
байт, слово, длинное слово и тетраслово и правильно корректируется указатель
стека SP. Напишите команду POP с аналогичными спецификациями.
9. Проанализируйте следующее макроопределение:
.MACRO PRINTCHRS STRING, LENGTH= #85
CVTWL LENGTH,— (SP)
PUSHAB . STRING
CALLS ' #2,PTCHRS
.ENDM PRINTCHRS
Покажите макрорасширения вызовов: a) PRINTCHRS LINE+8JR4, 6)
PRINTCHRS RECORD.
10. Макрокоманда PRINTCHRS в упр. 9 для фактической печати строки вызы-
вает процедуру PTCHRS. Считая, что PRINTCHRS правильно задает аргументы,
опишите список аргументов, ожидаемый PTCHRS. (Он не представлен в стандартном
формате списка аргументов.)
11. Вернитесь к упр. гл. б и просмотрите в руководстве VAX Architecture
Handbook информацию о сложении октаслов. Напишите макроопределение макро-
команды ADDQ2, по которой складываются два октаслова. Считайте, что фактические
аргументы определяются именами.
12. Напишите макроопределение макрокоманды RANGE, рассмотренной, в при-
мере 11.7.
13. Назовите несколько режимов адресации, которые будут применяться
как фактические аргументы для SOURCE и DEST в макрокоманде ABS из
примера 11.12. Укажите несколько режимов, применяемых для DEST неправильно.
14; Почему в макрокоманде PRINTMSG из примера 11.13 аргумент в виде
символьной цепочки запоминается по директиве .ASCIZ в памяти, а не в стеке?
15. Что происходит, если макрокоманда PRINTMSG из примера 11.13 вызыва-
ется без аргумента? Отмечаются ли ошибки времени ассемблирования и времени
выполнения или ошибок не будет? Что напечатает принтер в последнем случае?
16. Напишите макроопределение макрокоманды SETREG, по которой в каждый
регистр Rn загружается длинное слова п, 0<п^10. Макроопределение должно
быть коротким и не содержать отдельные операторы для каждого регистра.
17. Почему в макрокомандах ввода-вывода лучше вызывать процедуры,
выполняющие их действия, а не включать команды в сами макрокоманды?
18. Напишите макроопределение макрокоманды PUSH, включающей в стек
каждый из своих аргументов. Считайте, что в нее может входить любое число
аргументов (до 10) и что все аргументы представлены длинными словами.
19. Напишите макроопределение макрокоманды ARGLIST, по которой в памяти
формируется список аргументов процедуры в стандартной форме. Максимальное число
аргументов процедуры равно 10. Пусть формальными аргументами для ARGLIST
будут LABEL, ARG1, ARG2...... ARG10, где LABEL—метка списка аргументов
процедуры, a ARG1... ARG10 — аргументы процедуры (их можно считать адрес-
ными выражениями). Число аргументов процедуры не является аргументом ARGLIST.
20. Напишите определение макрокоманды PRINTCTR, которая имеет в качестве
аргумента символьную цепочку и печатает цепочку посередине строки из 80 симво-
лов. Пример макрокоманды: f
PRINTCTR <OUTPUT FROM TEST RUN>
(Макрокоманда может использовать PRINTCHRS.)
280
21. Назначение приведенной ниже макрокоманды SUM — сложить первые
три аргумента и поместить результат в четвертый аргумент. Она представляет
собой модификацию макрокоманды SUM из примера 11.15:
MACRO SUM A,B,C,TOTAL,T«=LXXX
LXXX-7.LENSTH (TOTAL) - 1
.IF IDN +,%EXTRACT(LXXX,1,TOTAL)
ADD'T'3 .IF.FALSE ADD'T'3 . ENDC A, В,"ZE X TRACT(0,LX X X,TOTAL) А,В,TOTAL
ADD'T'2 CvTOTAL
.ENDM SUM
а) Покажите макрорасширения следующих команд (приведите только машин-
ные команды, а не операторы условного ассемблирований):
1) SUM #5,(RS)+,R7,ALPHA '
2) SUM #5,RS,R7,(R1O)+
3) SUM R4,R5,R6,BETA+8
4) SUM #1,X,Y,—(R7)
б) Поясните назначение операторов условного ассемблирования в SUM; в част-
ности, укажите, почему они делают эту версию SUM лучше приведенной в примере
11.5.
в) Какие режимы адресации для TOTAL приведут к неправильному резуль-
тату?
Глава 12
Операции с двоичными
разрядами и полями
12.1. Общие замечания
Для всех описанных выше типов данных и для данных с плаваю-
щей точкой, которые будут обсуждаться в гл. 13, цепочка из восьми
или более битов образует одно данное. Команды оперируют такой
цепочкой как единым целым. В настоящей главе рассматриваются
команды, интерпретирующие свои операнды как цепочки независи-
мых битов или небольших групп битов. Команды, инициирующие
операции с двоичными разрядами (поразрядные), позволяют сдви-
гать двоичные цепочки, а также устанавливать, сбрасывать, до-
полнять (инвертировать) и проверять отдельные биты или указанные
группы битов операндов. Двоичными цепочками представлены флаги,
множества, любые из обычных типов данных, таблицы небольших
чисел, упакованных с целью экономии памяти, и т. д. — все, что
выберет программист.
Многие применения поразрядных команд связаны с программи-
рованием операционных систем. Поскольку мы не уверены в том, что
читатель знаком с операционными системами, ограничимся здесь
рассмотрением лиЩь двух простых примеров. Иллюстрируемые ими
приемы могут оказаться полезными во многих ситуациях.
Как уже отмечалось в гл. 1, иногда конкретную проблему нелег-
ко решить, пользуясь языком высокого уровня, и программист
вынужден обращаться к языку Ассемблера. К таким проблемам
относятся и те, для решения которых требуется применение
изучаемых в настоящей главе команд, поскольку среди операторов
языков высокого уровня редко встречаются операторы обработки
отдельных битов единиц памятй. Кроме этих операторов, отсутствую-
щих в языках высокого уровня, мы обсудим, как реализуются неко-
торые необычные типы данных и операции, например множества
в языке Паскаль.
В разд. 12.2 и 12.3 мы познакомим читателя с поразрядными
командами системы VAX, инициирующими типичные для больших
282
ЭВМ операции (хотя, как это неоднократно подчеркивалось, команды
системы VAX являются более многочисленными и гибкими), а
в разд. 12.4 — с множествами и операциями над ними.
В системе VAX имеется тип данных (двоичные поля переменной
длины) и команды, которых нет в большинстве ЭВМ, даже в новых
машинах с обширными системами команд. Эти команды, реализую-
щие операции над двоичными полями переменной длины, позволяют
упростить решение некоторых прикладных задач. Им посвящен
разд. 12.5.
Поясним некоторые термины, относящиеся к поразрядным (ло-
гическим) операциям. Бит считается установленным, или включен-
ным, если его значение равно единице; бит сброшен, или выключен,
если его значение равно нулю. Дополнением бита называется бит
с противоположным значением, т. е. дополнением 0 является 1,
а дополнением 1 оказывается 0. Маска — двоичный набор, в котором
установленные биты обозначают разряды (или представляемые
ими данные), анализируемые или обрабатываемые определенным
образом. Например, установленные биты в маске регистров показы-
вают, какие регистры включаются в стек. Маски, описываемые в
'настоящей главе, показывают, какие разряды другого операнда
обрабатываются по команде.
12.2. Простые
поразрядные операции
Здесь мы рассмотрим команды системы VAX, предназначенные
для проверки, установки, сброса и дополнения битов. В большинстве
команд в качестве одного из операндов используется маска,
определяющая, на какие биты второго операнда воздействует кон-
кретная команда.
По команде BIT (проверить бит) проверяются указанные биты
в двоичной цепочке и модифицируется код условия Z в зависимости
от того, все ли-проверяемые биты оказываются нулевыми. Формат
команды:
BITx маска, двоичная_цепочка
где х= В, W или L.
Пример 12.1. Проверка группы битов. Предположим, необходимо проверить,
не являются ли биты 14:7 в регистре R9 нулевыми. Маска должна содержать едини-
цы в разрядах 14:7, указывая проверку именно этих разрядов. Можно построить
маску, отмечая нужные разряды в соответствии с рис. 12.1. Проверка и условный
переход реализуются двумя командами:
BITL #aX00007F80,R9
BEQL ALL_ZERO
Поскольку все проверяемые биты находятся в младшем слове регистра,
для проверки можно воспользоваться более простой командой:
BITW #aX7F80,R9
283
R9
Разряды 31 ;_____________________________14_____________7______________О
/
Маска ООООООООООООООООО 11 1 1 1 1 1 10000000
» , ,v , * „ л ,—« , х -—
Маска 0000 7F80
(16-ричная)
Рис. 12.1. Построение маски
В приведенном примере и во многих других ситуациях маску
удобно определять как литеральный или непосредственный операнд.
Допускается любое основание системы счисления, но шестнадцате-
ричное представление обычно предпочтительнее, так как преобразо-
вание двоичного набора в шестнадцатеричное представление не
вызывает трудностей.
Поразрядные команды не обязательно воздействуют на группу
смежных битов. Если, например, нужно проверить, содержат ли все
нечетные разряды длинного слова PAIRS нули, следует воспользо-
ваться командой
BITL #ЛХАААААААА,PAIRS
и поместить после нее команду подходящего условного перехода.
Операнды в командах сброса, установки и дополнения битов
имеют один и тот же формат:
'BIS)
- В1СЬх2 маска, двоичная цепочка
1ХОИ
BIS]
ч В1Сг*3 маска, источник, получатель
,xor)
где х = В, W или L. Мнемоники этих команд означают «установить
бит», «сбросить бит» и «выполнить исключающее ИЛИ». Маска
всюду определяет те биты второго операнда, на которые воздействует
конкретная команда; остальные биты операнда не изменяются.
В двухоперандных форматах результат заменяет второй операнд.
В трехоперандных форматах результат операции над источником
запоминается в получателе; источник не изменяется (если его не
перекрывает получатель). Во всех трех командах коды условий
Z и N устанавливаются так же, как в операциях с целыми числами;
код условия V сбрасывается, а С не изменяется.
Пример 12.2. Дополнение битов. Предположим, что содержимое регистра
R7 составляет 083Е8АС1. После выполнения команды
XORL2 #aX0F003060,R7
его содержимое изменится на 073ЕВАА1. Дополняются биты 27:24, 13:12 и 6:5.
284
Пример 12.3. Варианты поразрядных операций. Следующей командой выключа-
ется бит 15 в регистре R4:
BICW2 #aX8000,R4
Чтобы выключить бит 15 в регистре R4 и поместить результат в регистр R8,
нужна команда.
BICL #aX00008000,R4,R8
Отметим, что во втором случае потребовалась команда, оперирующая длин.юм
словом, так как по команде BICW3 в регистр R8 будет скопирована только половина
результата.
Установить биты 22:18, дополнить биты 23 и 17 и сбросить первый и последний
байты регистра R10 можно по командам:
ВI8L2 «ИХ007С0000, R10
X0RL2 1ИХ00820000,R1О
ВICL2 t^XFFOOOOFFf R10
I Установить биты 22i18
I Дополнить биты 23 и 17
| Сбросить крайние байты
Однако в принципе для реализации указанных действий достаточно двух команд:
BI8L2 #^XFF7C00FF,R10 ; Установить биты 22t18 ; и крайни» байты
X0RL2 #*XFF8200FF,R10 । Дополнить биты 23 и 17
I и крайние байты
Пример 12.4. Преобразование кода ASCII десятичной цифры в двоичный код.
В разд. 7.4 для преобразования символьного кода в дополнительный сброс левой
тетрады символьного кода каждой десятичной цифры осуществлялся по команде
вычитания. В результате поручалось двоичное значение цифры. Для этих целей
применялась следующая команда, в которой регистр R6 содержал адрес обрабаты-
ваемого символа, а значение регистра R10 было сброшено до начала цикла
преобразования:
SUBB3 #48,(R6)+,R10 -.Образовать двоичную цифру
То же самое действие можно реализовать командой
BICB3 #aXF0,(R6)4-,R10 Образовать двоичную цифру
которая более понятна, чем предыдущая (не нужно задумываться над тем, для чего
выполняется вычитание и почему вычитается 48).
Пример 12.5. Возврат из процедуры флага кода условия. В гл. 9 было показано,
что процедура может возвратить флаг в вызывающую программу, устанавливая
любой из кодов условий в запомненном слове PSW, которое находится в стековом
кадре. Предположим, что процедура должна установить бит Z. Запомненное
слово PSW находится в первой половине второго длинного слова кадра вызова,
поэтому бит Z устанавливается по следующей команде:
BISB2#aB00000100,4(FP) Остановить бит Z
Логические операции. Можно считать, что биты представляют
собой логические переменные: значение 1 представляет TRUE (исти-
на), а значение 0 — FALSE (ложь). Следовательно, над битами и
цепочками битов можно выполнять стандартные бинарные операции
AND (обозначается как А или • ), OR (обозначается как V или
+ ) и EXCLUSIVE OR (обозначается как ф), а также унарную опе-
рацию отрицания или дополнения (обозначается как —|). Логические
операции показаны в виде таблиц истинности на рис. 12.2.
285
AND 0 1 OR 0 1
0 0 0 0 0 1
1 0 1 1 1 1
© 0 1 —1
0 0 1 0 1
1 1 0 1 0
Рис. 12.2. Таблицы логических функций
системы VAX, как установку, сброс
В большинстве ЭВМ име-
ются команды AND, OR и
XOR (или с другими мнемо-
никами) , которые выполняют
логические операции над
двоичными цепочками. (Ко-
нечно, эти операции специ-
фицированы для отдельных
пар разрядов; когда операн-
ды являются цепочками би-
тов, операция выполняется
независимо над парами би-
тов в соответствующих раз-
рядах двух операндов.) Хотя
мы описали такие команды
и дополнение битов, логические
операции OR и EXCLUSIVE OR реализуются командами: BIS и
XOR. Команда BIC очень близка к команде AND. Чтобы разобраться
в этих взаимосвязях, вернемся к разрядным операциям.
Операция, выполняемая по команде BIS, описывается следующей
таблицей:
Сравните эту таблицу с таблицей на рис. 12.2, которая опреде-
ляет операцию OR. Они полностью совпадают.
Обратимся к таблице операции EXCLUSIVE OR на рис. 12.2.
Пусть маска будет операндом, значения которого проставлены в
верхней части таблицы; значения второго операнда показаны слева.
В том столбце таблицы, где маска равна нулю, результаты анало-
гичны значениям второго операнда; а в столбце, где маска равна
единице, биты второго операнда дополнены. (Операции OR и
EXCLUSIVE OR являются коммутативными, поэтому не существен-
но, какой операнд считается маской.)
Таблица для операции, выполняемой по команде BIC, имеет вид:
286
Эта таблица не совпадает ни с одной из таблиц логических
операций на рис. 12.2. Однако читатель может легко проверить,
что операция, реализуемая командой BIC, совпадает с логической
операцией AND, выполняемой над дополнением маски и вторым
операндом.
Отрицание двоичной цепочки совпадает с ее инверсией. Значе-
ния всех битов изменяются на противоположные. Операция отрица-
ния выполняется по командам:
МСОМх источник, получатель
где х=В, W или L. В получателе запоминается инверсия источ-
ника; коды условий N и Z модифицируются так же, как в других раз-
рядных командах. Команду МСОМ можно заменить командой XOR,
если маска состоит из единиц, поэтому в ряде больших ЭВМ нет
специальной команды дополнения.
Пример 12.6. Макрокоманда AND. Если в прикладной программе выполняется
много логических операций, целесообразно предусмотреть специальную команду для
реализации логической операции AND. С этой целью определяется следующая
макрокоманда:
MACRO AND3 ОР1,0Р2,DEBT,TYPE-L
MCOM'.TYPE OP1,-(SP)
BIC'TYPE'3 <SP)+,0P2,DEST
. ENDM AND3
12.3. Команды сдвигов
и циклических сдвигов
По командам сдвигов биты перемещаются в регистре или ячейке
памяти. В больших ЭВМ обычно имеется несколько видов команд
сдвигов. В командах логических сдвигов все разряды интерпре-
тируются одинаково; ни один из них не играет особой роли. В ко-
мандах же арифметических сдвигов левый бит считается знаковым
и обрабатывается иначе, чем остальные. При логических сдвигах
биты, «выдвигающиеся» с одного конца операнда, теряются или
«вдвигаются» с его другого конца; в последнем случае говоря!1 о
циклических сдвигах.
В системе VAX предусмотрена одна команда циклического
сдвига ROTL (циклически сдвинуть длинное слово) и две команды
арифметического сдвига: ASHL (арифметически сдвинуть длинное
слово) и ASHQ (арифметически сдвинуть тетраслово) со следующим
общим форматом:
(ROTLJ
1 А5НЕ»счетч«к, источник,, получатель
(ASHQJ
Первый операнд, называемый счетчиком, представляет собой
байт, который определяет число и направление сдвигов. Число
287
сдвигов равно асболютному значению счетчика, а направление опре-
деляется его знаком: если значение счетчика положительно, произ-
водится сдвиг влево, а если отрицательно — вправо.
Команда циклического сдвига. Ниже показано Действие команды
ROTL в предположении, что производится сдвиг влево и число
сдвигов равно п. Циклический сдвиг вправо реализуется аналогич-
ным образом.
Команда ROTL инициирует установку или сброс кодов условий
Z и N, показывая, каким является результат — нулевым или отри-
цательным. Код условия V сбрасывается, а С'не изменяется.
Пример 12.7. Команда ROTL. Предположим, что содержимое регистра
R7 составляет 000013F5, а длинного слова в ALPHA—002ВС801. Рассмотрим сле-
дующую команду:
ROTL R7,ALPHA,ВЕТА
В каком направлении осуществлять сдвиг? Напомним, что счетчик есть байт,
поэтому он будет правым байтом регистра R7. Итак, значение счетчика равно F5
(десятичное число —11), и сдвиг производится вправо. Для нахождения результа-
та целесообразно выписать двоичное представление источника. На рис. 12.3 показа-
ны биты до и после циклического сдвига. Коды условий Z, N и V — нулевые.
(ALPHA):
Циклический
сдвиг на 11
разрядов
(ВЕТА) в результат: 00200579
) Рис. 12.3. Пример циклического сдвига
Пример 12.8. Необходимо поменять местами слова в регистре R8. Эта операция
выполняется по команде:
ROTL #16,R«,R8
Пример 12.9. Выделение двоичных полей. Допустим, нужно переслать биты
8:3 регистра R5 и биты 23:15 регистра R6 в правую часть регистра R10, сбросив
остальные биты последнего. Результат должен иметь вид:
288
Разряды
R10
31 14 6 5 0
0 ... 0 баз • • • 61363 . . . бз
т 4 1 v-------'
ИЗ R6 ИЗ R5
Задачу можно решить несколькими способами. Ниже приведен один из них в пред-
положении, что регистр R2 является рабочим
BICL3 #^XFFFFFE07,R5,R10 . 1 Выделить•биты 8:3 в RS
ROTL #-3,R10,R10 1 Сдвинуть их вправо
BICL3 #*XFF007FFF,R6,R2 1 Выделить биты 23:15 в R6
ROTL #-9,R2,R2 1 Передать в разряды 14:6
BISL2 R2,R10 1 Объединить два сегмента
Пример 12.10. Выключение бита. Рассмотрим процедуру TURNOFF на рис. 12.4.
Используемые в ней способы обработки разрядов полезны и во многих других ситуа-
циях (некоторые из них предлагаются в упражнениях). В разд. 12.5 показано,
что с помощью команд, реализующих операции над двоичными полями переменной
длины, эту процедуру можно сделать более эффективной.
.PSECT TURNOFF
f
: ПРОЦЕДУРА TURNOFF (BIT-STRING,POSITION)
?
; Процедура TURNOFF находит и выключает самый правый включенный бит
; своего первого аргумента - двоичной цепочки (длинное слово)• Она
; возвращает номер позиции измененного бита во втором аргументе -
; байте. (Если нет включенных бит, она возвращает 32.)
?
; МЕТОД
; Копия двоичной цепочки циклически ^сдвигается до нахождения включенного
; бита, причем каждый бит проверяется, когда он находится в младшем раэ-
; ряде. Счетчик цикла следит за исходной проверяемой позицией и испольэу-
; ется для построения маски, с помощью которой выключается бит в исходной
; двоичной цепочке.
I
; Смещения для списка аргументов
STRING = 4
POSN = 8
5
; ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ: R6 двоичная цепочка
; R7 счетчик цикла
? R8 маска для выключения бита
.ENTRY TURNOFF,ЛМ< R6,R7,R8 >
10 Зак. 821
289
i
MOVL «STRING(AP),R6 5 Взять двоичную цепочку
BEQL ZERO Перейти, если все биты выключены
CLRL R7 ? Инициализировать счетчик цикла
?
TEST: BLBS R6,FOUND Проверить младший бит
ROTL #-l,R6,R6 Взять следующий бит
AOBLEQ *31,R7,TEST Управление циклом
1
FOUND: ROTL R7,#1-,R8 Переслать единицу в позицию
BICL2 R8,«STRING(AP) Выключить бит
MOVB R7,«P0SN(AP) Запомнить номер позиции
5
RET Возврат
5
ZERO: MOVB RET .END #32,«P0SN(AP) ; Запомнить позицию 32 1 Возврат Рис. 12.4. Процедура TURNOFF
Арифметические сдвиги. Команды ASHL и ASHQ называются
командами арифметических сдвигов, так как их операнды-источники
представляются целыми числами в дополнительном коде, а действия
их эквивалентны умножению и делению на степени числа 2. Эти
команды влияют на коды условий N, Z и V, как и другие команды
целочисленной арифметики. Конечно, операции арифметических
сдвигов можно реализовать командами MUL и DIV. В некоторых
ЭВМ есть даже более эффективные команды арифметических сдви-
гов, однако команды ASHL и ASHQ очень важны при манипуля-
циях двоичными цепочками. Именно поэтому они описываются здесь,
а не в гл. 6.
Чтобы показать взаимосвязь операций сдвигов и умножения
(деления), рассмотрим обычные десятичные числа. Для умножения
целого десятичного числа на 10 или степень 10 необходимо приписать
к числу справа нули. Деление же целого десятичного числа на
степень.10 (с усечением) осуществляется путем «стирания» справа
соответствующего количества цифр. Умножение и деление целых
двоичных чисел на степени числа 2 реализуются аналогичным,
образом. Поскольку целое число находится в конкретной ячейке
памяти или в регистре, невозможно точно добавлять или стирать
цифры справа; приходится сдвигать число в занимаемой им единице
памяти, отбрасывая несколько битов с одного конца и заполняй
освобождающиеся биты с другого конца. При умножении сдвиг про-
изводится влево и освобождающиеся биты справа заполняются
нулями. Для деления сдвиг осуществляется вправо и освобождаю-
щиеся биты слева заполняются копией знакового бита. Следователь-
290
но, команда ASHL со счетчиком 3 эквивалента умножению на
23 или 8; если значение счетчика равно —16, должно осуществлять-
ся деление на 2е или 64.
Операции арифметических сдвигов иллюстрируются приведенны-
ми ниже диаграммами.
Арифметический сдвиг влево (умножение на степени числа 2):
заполняются нулями
отбрасываются
Отметим, что если хотя бы один отброшенный* слева бит не
равен исходному знаковому биту или если знаковый бит сдвинутого
результата не совпадает с исходным знаком, происходит перепол-
нение результата (как целого числа в дополнительном коде) и уста-
навливается код условия V.
Арифметический сдвиг вправо (деление на степени числа 2):
заполняются копией
знакового бита
отбрасываются
Пример 12.11. Арифметический сдвиг. Предположим, что содержимое длинного
слова в А1 составляет FFFFF4A2 до выполнения команды
ASHL #—6.А1.А2
Длинное слово в А1 сдвигается на шесть разрядов вправо. Анализируя двоичный
набор в А1, определяем результат:
FFFFF4A2 =
Сдвиг вправо,
отбрасываются
правые 6 бит,
заполняются
единицами слева:
11 . . . 11010010100010
Преобразование FFFFFFD2
в 16-ричное:
Таким образом, в длинном слове по адресу А2 запоминается FFFFFFD2.
Проверим результат, произведя деление. Сдвиг вправо на шесть разрядов должен
соответствовать делению на 26 или 64. Значение длинного слова в А1 равно —29Ю10.
Деление на 64 дает примерно —45,5. Команды целочисленного деления усекают
результат к нулю, и, по всей вероятности, мы должны получить —45. Однако
чирло FFFFFFD2 есть —46
Как показывает пример 12.11, при арифметическом сдвиге вправо,
интерпретируемом как операция деления, отрицательный результат
всегда усекается вниз, от нуля. Этим арифметический сдвиг несколько
отличается от операции деления, выполняемой по команде DIV.
10* 291
Иногда маски для поразрядных команд, рассмотренных в разд.
12.2, необходимо формировать во время выполнения, так как требуе-
мый двоичный набор зависит от имеющихся данных. Пример образо-
вания масок будет приведен в разд. 12.4, а мы пока рассмотрим
пример 12.12, где показано, как сформировать маску в общей форме.
Пример 12.12. Формирование маски с помощью команды ASHL. Допустим,
необходимо построить маску, содержащую п единиц, начиная с разряда р; другими
словами, биты (р + п— 1):р должны быть единицами, а остальные — нулями. Ниже
представлены команды, построенные в предположении, что если знаковый бит со-
держит единицу, то при арифметическом сдвиге вправо освобождающиеся биты
заполняются единицами. Воспользуемся командой ASHL для генерирования требуе-
мого числа единиц, а затем с помощью команды ROTL сдвинем их в нужные
разряды. Будем считать, что содержимое регистра R6 составляет п, содержимое
регистра R7 равно р, маска формируется в регистре R8, а регистр R3 является рабочим.
8UBB3 Счетчик сдвигов я -(п-1)
ASHL R3,ФИХЙООООООО,R8 | Образовать п единиц
ADDB3 R6,R7,R3 | р + п
ROTL R3fR8,R8 । Сдвинуть единицы в разряды p+n-lip
Эту же задачу можно решить, применив всего две команды, с которыми вы
познакомитесь в разд. 12.5. Однако показанный здесь метод с небольшими моди-
фикациями реализуется и в других ЭВМ, в то время как команды, описываемые
в разд. 12.5, в большинстве ЭВМ отсутствуют.
12.4. Множества (пример)
Разрядные операции весьма удобны для манипуляций множества-
ми, представленными двоичными цепочками. Будем полагать, что
всё обрабатываемые множества являются подмножествами фикси-
рованного универсального множества, содержащего 32 элемента.
Каждому элементу универсального множества присваивается индекс
между 0 и 31. Следовательно, можно записать:
Универсальное множество={х0, Хь х2, ...» *29» *эо, Х31}.
Множество S представляется двоичной цепочкой, в которой
каждый бит показывает, входит ли в S элемент универсального
множества с тем же индексом. Иными словами, S представляется
как
Ьз| 630 £>2д ••• ^2 ^0»
где Г 1, если x^S,
1
L 0, если xi^S.
Если, например, двоичная цепочка для S имеет вид
00010000010111000100000010000001,
ТО S=(xo, Х7, Х14, Х18, Х19, Х20, Х22, Х28|.
Именно таким способом в языке Паскаль реализуется тип данных
SET (множество). Предельный размер множества в языке Паскаль
обычно равен размеру слова ЭВМ или кратен ему. (Язык Паскаль
292
системы VAX-11 допускает множество максимум из 256 элементов,
для представления которых используются восемь длинных слов.)
Предположим, что зарезервировано пространство для множеств
S, Т, V, и множества S и Т некоторым образом инициализированы.
Примем, что имена множеств — это метки длинных слов, которые
представляют множества. Необходимо реализовать основные опера-
ции над множествами (объединение, пересечение, дополнение и др.)
Элемент принадлежит объединению S и Т, если он содержится
в любом из них (или обоих). Следовательно, операция объединения
является логической операцией OR над представлениями множеств:
BISL3 S, Т, V ;У=объединение S и Т
(Маской может быть S или Т, так как OR — это коммутативная
операция.)
Пересечение двух множеств содержит все элементы, находящиеся
в обоих множествах, поэтому оно вычисляется с помощью логи-
ческой операции AND- над представлениями множеств. Система
VAX не имеет команды AND, но можно воспользоваться макро-
командой AND3 из примера 12.6:
AND3 S, Т, V;V= пересечение S и Т
Дополнение множества вычисляется с помощью команды
MCOML.
Возможно, нам придется выполнить несколько проверок. Напри-
мер, может потребоваться определить, равны ли два множества,
является ли одно из них подмножеством другого, входит ли кон-
кретный элемент в то или иное множество, не пустое ли данное
множество. Иногда необходимо установить число элементов множе-
ства.
Проверка двух множеств на равенство легко реализуется коман-
дами CMPL и BEQL (или BNEQ).
Как определить, что Sc Г? Конечно, можно написать цикл, ко-
торый по очереди выделяет каждый бит S и проверяет, находится
ли он также и в Г. Для этого потребуется несколько операторов
и 32 прохода по циклу. Более эффективный путь — выразить прове-
ряемое условие в другой форме:
SczT если и только если S\)T=T.
Читателю предлагается самостоятельно написать команды для про-
верки условия S|J7'=7’.
Подсчет элементов множества выполняется с помощью цикла,
аналогичного циклу в процедуре, приведенной на рис. 12.4.
Со всеми перечисленными выше проверками вы подробнее позна-
комитесь в упражнениях.
Задача инициализации представления множества в виде двоичной
цепочки оказывается более сложной, чем операции над множествами.
На рис. 12.5 показана процедура, которая инициализирует множество
293
в предположении, что универсальное множество состоит из целых
чисел в диапазоне 0—31 и что конкретные элементы инициализируе-
мого множества считываются с терминала как строка в свободном
формате. Процедура вызывает другую процедуру GETNUM для
локализации входных чисел в строке и преобразования их представ-
ления в дополнительный код. Процедура GETNUM описана в
упр.. 9.17.
.PSECT SET UP
I
I ПРОЦЕДУРА SET.UP (SET)
I
* Эта процедура инициализирует аргумент SET (длинное слово) как предстае-
I ленив о виде двоичной цепочки множества целых чисел, вводимых о одной
I строке с терминала. Целые числа должны быть в диапазоне от О до 31.
I* Представление таково, что бит 1 включен, если i находится в множестве.
I
I SET.UP вызывает процедуру GETNUM для нахождения каждого числа во входной
I строке и преобразования его в дополнительный код. GETNUM возвращает в RO
I флаг s 1- число найдено и преобразовано. О-в противном случае. SET.UP
I предполагает, что если GETNUM возвращает флаг О, есе элементы множества
I обработаны.
I
I SET.UP возвращает о R0 флаг успеха/неудачи. Если множество инициализиро-
I вано без проблем, R0 будет содержать 1. Если хотя бы одно из введенных
I чисел находится вне диапазона 0-31, R0 будет содержать О и аргумент
I (длинное слово) не заполняется.
I
LINES .BYTE ЛХ2ОС813 у Пространство для входной строки
ARGSs .LONG 4 | Список аргументов для GETNUM
.BLKA 2 ; Начальный и конечный адреса
.ADDRESS NUMBER,RESTART
NUMBERS .BLKL 1 | Элемент множества
RESTARTS .BLKA 1 | Начало следующего поиска
I ИСПОЛЬЗОВАНИЕ PEFHCTPOBs R6 представление множества
I R7 маска
.ENTRY SET.UP,*M<Rb,R7>
I Инициализация
CLRL R6 | Вначале множество пустое
READLINE LINE+1 | Ввести элементы множества
MOVAB LINE,RESTART । Начальный адрес первого поиска
ADDL3 DLINE+1,R0,ARGS+8 | Конечный адрес входной строки
? •->
I
I Взять следующий элемент множества из входной строки.
I
NEXTs MOVL RESTART,ARGS*4 > Где начинается следующий поиск
CALLG ARGS,GETNUM , ; Взять следующее входное число
BLBC RO,DONE ; Перейти, если множество завершено
294
I Проверить, что число в правильном диапазоне 0-31
TSTL BLSS CMPL BGTR NUMBER | BAD | NUMBER,#31 | BAD | j Проверить знак номера элемента 1 Перейти, если отрицательный 1 Сравнить с верхним пределом 1 Перейти, если больше 31
1 1 Ввести элемент в множество
1 ASHL NUMBER,#!,R7 | 1 Построить маску
1 BXSL2 R7,R6 । 1 ВключиУь бит в множестве
* BRB NEXT । 1 Перейти к другому элементу
1 Запомнить представление множества 2 и установить флаг успеха/неудачи.
DONE» MOVL R6,S4(AP) । Запомнить' представление множества
MOVL #1,RO । Установить флаг успеха
RET J Возврат
BAD* CLRL RO । Установить флаг неудачи
RET .END 1 Возврат
Рис. 12.5. Процедура инициализации множества
12.5. Двоичные поля
переменной длины
Формат данных. Двоичное поле переменной длины представляет
собой цепочку из 0—32 смежных битов, которая не обязательно
начинается на границе байта. Следовательно, она может занимать
от нуля до пяти смежных байтов. Двоичные поля переменной
длины удобны для упаковки нескольких данных небольшой длины
при хранении их в памяти, для работы с двоичными таблицами,
для манипуляций (например, преобразования) представлениями дан-
ных и для решения других прикладных задач.
Операции с двоичными полями системы VAX можно выполнить,
комбинируя соответствующим образом уже рассмотренные выше ко-
манды (в частности, из разд. 12.2 и 12.3), но, разумеется, более
эффективный метод — применить-для этих целей специальные коман-
ды. Кроме того, в некоторых ситуациях очень важным является
и то обстоятельство, что специальные команды игнорируют границы
байтов и даже длинных слов.
Для определения двоичного поля переменной длины используются
три операнда: базовый адрес, смещение от этого адреса и размер
двоичного поля. Базовый адрес — это адрес байта в памяти или
имя регистра. Смещение (или позиция) представляет собой позицию
первого бита в двоичном поле относительно первого бита по базо-
295
вому адресу, т. е. число битов (а не байтов), разделяющих первый
бит двоичного поля и первый бит байта по базовому адресу. Смеще-
ние задается как целое число в дополнительном коде в формате
длинного слова. Иными словами, оно может быть положительным
или отрицательным и очень большим по величине. Размер двоичного
поля определяет число битов в двоичном поле и представлен байтом.
Rn +1 Rn
31 14 0 31 22
Двоичное поле, определяемое следующим образом:
позиция s 22, размер s 25, база = Rn
б)
Рис. 12.6. Определение двоичного поля переменной длины:
а) в памяти, б) в регистрах
На рис. 12.6 показано, каким образом три операнда определяют
двоичное поле. Отметим, что на диаграммах двоичных полей байты
с младшими адресами находятся справа, так как в командах системы
VAX младший бит 0 считается первым битом единицы памяти или
регистра; следовательно, бит 0 байта с адресом А (для любого адре-
са А) находится сразу после левого бита 7 байта с адресом А—1.
Первым битом двоичного поля считается его правый бит.
Если базовым адресом двоичного поля определен регистр Rn,
а поле выходит за бит 31 регистра Rn, поле продолжается с бита О
регистра Rn+ 1. Поэтому на диагра'ммах регистр Rn+ 1 показан сле-
ва от регистра Rn. ,
Три операнда машинных команд, инициирующих операции с
двоичными полями переменной длины, всегда следуют в таком по-
рядке:
позиция, размер, базовый адрес
В командах манипуляций двоичными полями возникают ошибки
зарезервированного операнда, если размер поля не находится в
296
диапазоне 0—32 или, если базовым адресом определен регистр,
позиция поля больше, чем 31, и его размер не нулевой (т. е. если
непустое поле не начинается в указанном регистре). Если размер = О,
двоичное поле считается пустым. Большинство команд допускает
естественную интерпретацию пустого поля.
Команды «найти первый». Команды FFS (найти первый уста-
новленный бит) и FFC (найти первый сброшенный бит) сканируют
двоичное поле слева направо и локализуют первый установленный
или сброшенный бит. Если такой бит найден; номер его позиции
помещается в операнд-получатель (длинное слово), и код условия Z
сбрасывается. Позиция вычисляется относительно того базового ад-
реса, который определяет положение двоичного поля. Если в поле
не найден бит в указанном состоянии, в операнд-получатель
помещается номер бита, находящегося за концом (левее) двоичного
поля, и Z устанавливается. (Как и следует ожидать, если размер = О,
то Z устанавливается, а получателю присваивается значение, рав-
ное позиции.)
Обе команды имеют следующий формат:
FFx позиция, размер, база, получатель
где x = S или С.
। Пример 12.13. Действие команд FFS и FFC. Пусть память имеет следующее
содержимое: ALPHA
F0 83 48 01 00 00 07 FE 24 44 89
Ниже показаны несколько команд и результаты их выполнения.
Команда Двоичное Результат
поле В R8 Z
FFS#56,#24,ALPHA,R8 . 480100I6 64 0
FFS#39,#3,ALPHA+2,R8 002 42 1
FFC#0,#32, ALPHA,R8 24440089I6 1 0
FFC#35,#10, ALPHA,R8 00111111112 43 0
Пример 12.14. Новый вариант процедуры TURNOFF. В разд. 12.3 приведена
процедура TURNOFF (рис. 12.4), которая находит самый правый установленный
бит в длинном слове, выключает этот бит и возвращает как аргумент его позицию.
В новом варианте процедуры, представленном на рис. 12.7, для нахождения бита
применяется команда FFS. Цикл поиска и инициализация (частично) из преды-
дущей процедуры оказываются ненужными.
.PSECT TURNOFF_2
5
| ПРОЦЕДУРА TURN0FF.2 (BIT.STRINB^ POSITION)
I
j Эта процедура находит и выключает самый правый включенный бит
I в двоичной цепочке (длинное слово), являющейся первым аргументом.
; Она возвращает номер пооиции измененного бита во втором аргументе
I (байте). (Если включенных бит нет, процедура возвращает 32.)
I
| МЕТОД
I
297
j Для нахождения первого включенного бита используется команда FF8,
I оперирующая двоичным нолем переменной длины. (Отметим, что при
I отсутствии установленных бит команда FF8 возвращает как номер
I позиции 32.) Маска для выключения бита образуется циклическим
I сдвигом 1 в нужную позицию.
Р ~ _
; Смещения для списка аргументов
STRING - 4
POSITION - 8
5
; ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ! R7 позиции первого установленного бита
; R8 маска для выключение бита
1
.ENTRY TURNOFF 2,*M<R7,R8>
1
FF8 *0,#32,«STRING(AP) , R7 । Найти первый установленный бит
BEQL P08N 1 Перейти, если нет установленных
ROTL R7,#1,R8 1 Сдвинуть на одну позицию
ВICL2 R8,«STRING(AP) 1 Выключить бит
POSNi MOVB R7,«POSITION(AP) 1 Запомнить номер позиции
RET
.END
Рис. 12.7. Процедура TURNOFF_2
Пример 12.15. Двоичная таблица дисковой карты.
Проблема. В операционных системах для регистрации состояния каждого блока
на диске часто применяется двоичная таблица. Предположим, что BLOCKS—
число блоков на диске. Блоки нумеруются от 0 до BLOCKS—1. Дисковая карта
представляет собой цепочку из BLOCKS битов, в которой бит / установлен, если
блок i занят, и сброшен, если блок / свободен. Проблема заключается в том, чтобы
найти первый сброшенный бит и, следовательно, номер первого дискового блока,
доступного для запоминания данных или программ.
Обсуждение возможного решения. Максимальный размер поля переменной дли-
ны равен 32, но дисковая карта содержит несколько сотен битов, поэтому потре-
буется цикл. Очевидно, в цикле необходимо выполнять четыре операции: скани-
рование сброшенного бита (FFC), проверку (найден ли такой бит), инкремент
номера позиции на 32 для перехода к сканированию следующего поля (если не
найден) и контроль сканирования (не вышло ли оно за конец таблицы; если это
произошло,—в таблице нет сброшенных битов). Так как желательно обеспечить
максимальное быстродействие программ операционной системы, цикл сканирования
должен быть возможно короче (например, состоять всего из двух комад). Еслц
по команде FFC сброшенный бит не найден, в получатель загружается позиция
бита, который находится за просмотренным полем. Именно с этой позиции должно
начаться следующее сканирование, поэтому один и тот же регистр можно ис-
пользовать для определения позиции и операнда-получателя. В цикле же потребуется
проверять окончание таблицы, если построить ее с нулевым байтом в конце для
форсирования выхода из цикла.
298
Решение!
BLOCKS число блоков на диске
DISK.MAP: .BLKB BL0CK8/8+1
I Таблица дисковой карты
CLRL R6 1 Начальная позиция О
SCANi FFC R6,#32,DISK.MAP,R6 1 Искать сброшенный бит
BEQL SCAN 1 Повторить, если не найден
CMPL R6,#BL0CK8 1 Проверить, не заполнен ли диск
BGEQ DISK.FULL
I R6 содержит номер доступного дискового блока.
Введение и выделение двоичных полей. Команды 1NS (ввести)
и EXT (выделить) применяются для построения и декомпозиции
длинных двоичных цепочек, образованных из нескольких двоичных
полей переменной длины. Они имеют следующие форматы:
INSV источник, позиция, размер, база
EXTV позиция, размер, база, получатель
EXTZV позиция, размер, база, получатель
По команде INSV биты (размер —1): 0 длинного слова операнда-
источника вводятся в двоичное поле, определяемое остальными
операндами. Она не влияет на коды условий. По команде EXTV
выделяется (копируется) указанное двоичное поле, расширяется
его левый (знаковый) бит и образовавшееся длинное слово поме-
щается в операнд-получатель. Следовательно, команда EXTV интер-
претирует двоичное поле как целое число в дополнительном коде.
Команда EXTZV (выделить двоичное поле переменной длины с
нулевым расширением) реализует аналогичную операцию, но при
этом лишние биты получателя заполняются нулями, а не копиями
левого бита двоичного поля. Обе команды EXT интерпретируют
двоичное поле размером = 0 как содержащее целое число 0. Они
влияют на коды условий N и Z обычным образом, сбрасывают
бит V и не изменяют бит С.
Пример 12.16. Выделение двоичных полей. В примере 12.9 с помощью разряд-
ных команд двоичные цепочки выделялись из двух регистров и упаковывались
в третий. Пользуясь командами EXT и INS, можно удалить команды ROTL. Дей-
ствие каждой команды иллюстрируется рис. 12.8.
299
EXTZV #3,#6,RS,RIO । Выделить биты 8i3 RS
EXTZV #15,#9,R6,R2 j Выделить биты 23i15 в R6
INSV R2,#6,#9,R10 | Вставить биты R6 В RIO
Пример 12.17. Графика двоичных матриц. В некоторых графических терминалах
изображение, выводимое на экран, представляется в памяти как двоичная матрица.
Каждый бит соответствует одной точке или элементу (pixel) экрана; бит установлен,
если соответствующий элемент включен (светится). Для получения на экране изоб-
ражения необходимо установить соответствующие биты в матрице.
Будем считать, что размеры экрана 256 x 256 и матрица занимает 256 x 8 длин-
ных слов, как показано на рис. 12.9. Отметим, что длинное слово с наименьшим
адресом находится в нижнем правом углу. Теперь размещение длинных слов
(справа налево) становится понятным: такое размещение обеспечивает соответствие
соседних битов в памяти и соседних точек на экране терминала. Слова на экране
отображены в порядке возрастания их адресов (снизу вверх).
Последнее длинное слово
16-е длинное слово 9-е длинное слово
8-е длинное СЛОВО' Первое длинное слово
Строка 255
Строка 1
Строка 0
Рис. 12.9. Двоичная матрица для получения графического изобра-
жения на экране
300
Очевидно, что при вычерчивании на экране линий можно не учитывать границы
длинных слов. В этом состоит одно из преимуществ использования команд, реали-
зующих операции над двоичными полями переменной длины, применительно к дан-
ному случаю. Кроме того, если точно определить обрабатываемое двоичное поле,
то другие участки изображения изменяться не будут.
Рис. 12.10. Изображение на экране сплошного прямо-
угольника
Установим биты, соответствующие на экране сплошному прямоугольцику, кото-
рый является, например, частью диаграммы. Предположим, что размещение прямо-
угольника задается номером нижней строки и смещением его правой стороны отно-
сительно правого края экрана, а размеры — числом элементов изображения по гори-
зонтали и вертикали. Пусть максимальная ширина прямоугольника равна 32 эле-
ментам (рис. 12.10). Так как каждая строка на экране представляется цепочкой
смежных битов, будем срисовать» прямоугольник по строкам, начиная снизу. Первый
байт экранной матрицы является базовым адресом для команд, реализующих опера-
ции над двоичными полями. Процесс построения прямоугольника описывается
следующим алгоритмом:
нижняя___позиция: = нижняя__строка ♦ 256 + правая_сторона
for позиция: = нижняя__позиция step 256
to нижняя___позиция + (высота —1) *256 do
Вставить двоичную цепочку
Приведенный ниже программный сегмент «рисует» прямоугольник.
SCREEN! .BLKL 8«2S6
ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ! R6
R7
RB
R9
номер строки низа прямоугольника
высот* прямоугольника (в Ритам)
смещения правой стороны
(в Витах) от правого края жрана
ширина прямоугольник* (в битам)
Предполагается, что в указанные регистры уже загружены необходимые данные.
R1O позиция (индекс цикла)
R11 предел цикла
MULL3 R6,#236,R1O | Низ*256
ADDL2 R8.R10 1 Позиция для низа
SUBL3 #1,R7,RU 1 Высота-1
301
ASHL #a,Rll,Rll ) (Высота-1)«296
ADDL2 R1O,R11 | Продол цикла
I
DRAWi INSV ITXFFFFFFFF,RIO,R9,SCREEN
I Вставить двоичную цепочку
ACBL RS,#256,RIO,DRAW
I Инкремент позиции и порокод
Сравнение двоичных полей. По командам сравнения двоичные
поля интерпретируются как целые числа и сравниваются с целыми
числами в формате длинного слова. (Двоичное поле размером =0
считается целым числом 0.) Главное назначение команд сравнения—
воздействие на коды условий. Эти команды особенно удобны в
тех ситуациях, когда с целью экономии памяти используются упа-
кованные небольшие числа, содержащие менее восьми битов. Фор-
маты команд:
CMPV позиция, размер, база, длинное__слово
CMPZV позиция, размер, база, длинное_слово
По команде CMPV (сравнить двоичное поле переменной длины)
выделяется двоичное поле и расширяется его левый бит до образо-
вания длинного слова; следовательно, двоичное поле интерпретиру-
ется как целое число в дополнительном коде. По команде CMPZV
(сравнить двоичное поле переменной длины, имеющее нулевое расши-
рение) выделяется двоичное поле и дополняется слева нулями до
длинного слова; таким образом, поле интерпретируется как без-
знаковое целое число. В обоих случаях расширение осуществляется
в рабочем регистре CPU. При этом поля и соседние биты не моди-
фицируются. Воздействие рассматриваемых команд сравнения на
коды условий аналогично воздействию на них команд СМР сравне-
ния целых чисел. Бит N устанавливается, если первый операнд
(т. е. расширенное двоичное поле) меньше последнего операнда—
длинного слова, а бит Z—если операнды равны. Бит V сбрасыва-
ется, а бит С устанавливается, если первый операнд меньше
длинного слова, когда оба они считаются беззнаковыми целыми
числами.
Пример 12.18. Сравнение двоичных полей. Предположим, что участникам
викторины было задано по 10 вопросов, на которые требовалось ответить по пяти-
балльной системе. Ответы всех участников хранятся в длинных словах, и на каждый
ответ отведено по три бита. Формат длинного слова:
29 27 ... 5 3 2 0
Q10 Q9 Q3 Q2 Q1
Пусть адрес длинного слова $ ответами одного из участников викторины нахо-
дится в регистре R8. С помощью следующих команд можно проверить, что ответ
на седьмой вопрос оценивается как 4 или 5.
CMPV # 18,#3, (R8) ,#4
BGEQ HIGH
302
Поразрядные команды
Таблица 12.1
Команда Примечание Операция
В 1Тх маска, двоичная цепочка BISx2 маска, двоичная цепочка BISx3 маска, источник, получатель В1Сх2 маска, двоичная__цепочка В1СхЗ маска, источник получатель XORx2 маска, двоичная цепочка XORx3 маска, источник, получатель МСОМх источник, получатель ROTL счетчик, источник, получатель ASHL счетчик, источник, получатель ASHQ счетчик, источник, получатель Х=В, W ИЛИ L х=В, W или L х=В, W или L х = В, W или L х=В, W или L х=В, W или L х=В, W или L х = В, W или L Счетчик — байт; п = 1счетчик! Сдвиг влево, если счетчик>0; Сдвиг вправо, если счетчик<0 Установить код условия Z, если все биты нулевые Установить указанные биты (логи- ческое OR маски и второго операнда) Сбросить указанные биты (логичес- кое AND дополненной маски и второ- го операнда) Дополнить указанные биты (логи- ческое EXCLUSIVE OR маски и вто- рого операнда) Инверсия источника-^получатель Циклически сдвинуть источник на п разрядов Арифметически сдвинуть на п разрядов Арифметически сдвинуть нал разрядов
12.6. Заключение
Поразрядные команды инициируют логические операции над
двоичными цепочками, т. е. в них каждый бит операнда считается
независимым от остальных. К разрядным операциям относятся про-
верка, установка, сброс, дополнение и сдвиг битов. Во многих пораз-
рядных командах один из операндов является маской — двоичным
набором, который определяет, какие биты другого операнда подлежат
обработке. Поразрядные команды, рассмотренные в настоящей гла-
ве, приведены в табл. 12.1.
Одно из применений поразрядных команд связано с операциями
над множествами, представленными в виде двоичных цепочек.
Поразрядные команды также удобны для работы с флагами, преоб-
разований данных, упаковки небольших данных, манипуляций двоич-
ными таблицами и т. д.
В системе VAX имеется тип данных, называемый двоичным
полем переменной длины. Двоичное поле переменной длины представ-
ляет собой цепочку из смежных битов (0—32), которая не обяза-
тельно начинается или заканчивается на границах байтов или длин-
ных слов. Поле определяется своим базовым адресом, номером
позиции первого бита и. размером (в битах). Команды, реализую-
щие операции над такими полями, в большинстве ЭВМ отсутствуют.
Манипуляции двоичными полями можно реализовать и с помощью
поразрядных команд. Рассмотренные в настоящей главе команды
сведены в табл 12.2.
Команды, реализующие операции
над двоичными полями переменной длины
Таблица 12.2
Команда Операция
FFx позиция, размер, база, полу- чатель x=S ил и С INSV источник, позиция, размер, база EXTV позиция, размер, база, полу- чатель EXTZV позиция, размер, база, полу- чатель CMPV позиция, размер, база, длин- ное слово CMPZV позиция, размер, база, длин- ное слово Найти первый установленный бит (S) или первый сброшенный бит (С). Z = 0, если найден; Z=l, если не найден Скопировать биты (размер —1):0 источника в поле Скопировать поле в получатель, расширить знак Скопировать поле в получатель, в расшире- нии 0 Сравнить поле (имеющее расширение знака) с длинным словом Сравнить поле (имеющее нулевое расшире- ние) с длинным словом
304
12.7. Упражнения
1. Напишите команды, по которым устанавливаются биты 5, 8 и 17 в регистре
R9, а все остальные сбрасываются.
2. Напишите команды для дополнения всех битов во втором байте (справа)
регистра R10 и загрузки результата в R11.
3. Напишите команды для сброса четных разрядов в регистре R4, установки
разрядов 3, 19 и 31, дополнения бита 15 и сохранения остальных разрядов неизмен-
ными.
4. Напишите команды копирования младшего байта регистра R8 в каждый из
остальных байтов регистра R8.
5. В примере 12.4 показаны два способа преобразования символьного кода
десятичной цифры в двоичный. Предположим, что преобразуемая символьная
цепочка вместо ведущих (левых) нулей содержит пробелы. Будет ли получен
правильный результат при использовании каждого из этих способов? Ответ
поясните.
6. По командам BIT, BIS, BIC и XOR проверяются, устанавливаются, сбра-
сываются или дополняются биты второго операнда, определяемые маской (первым
операндом). По командам МСОМ выполняется такая же операция, как и по команде
XOR, но над всеми битами операнда-источника, и маска в этом случае не нужна.
Для каждой из команд BIT, BIS и BIC напишите команды, по которым выпол-
няются те же операции, но над всеми битами и без маски.
7. Напишите команды для сброса самого правого включенного бита в регистре
R8. Воспользуйтесь всего двумя командами, не привлекая цикл и команды, реали-
зующие операции над полями переменной длины. (Процедура на рис. 12.4 для реше-
ния этой задачи выполняет больше операций, так как необходимо определить номер
позиции нужного бита.)
8. Напишите команды для установки всех битов в правой цепочке нулей,
если правый бит регистра R7 содержит нуль, и для того, чтобы цепочка осталась
неизменной, если этот бит содержит единицу. Например, пусть цепочка
00001010111010100001111011000000
превратится в цепочку
00001.010111010100001111011111111.
(Эту операцию можно выполнить с помощью всего двух команд.)
9. Каков будет результат выполнения команд ROTL, если значение счетчика
равно 0? Каково действие команды ASHL при том же значении счетчика?
10. Напишите команды для обмена содержимого двух правых байтов в регистре
R9. Воздействие на остальные два байта особой роли не играет.
11. Напишите машинный код следующих команд: a) ROTL #—1, R9, R3,
б) ROTL #1, R9, R3, в) ASHL R3, #АХ80000000, R8, г) ASHQ #63, R7, R3.
12. Рассмотрите следующий программный сегмент:
MOVL *32,R6 1 Инициализировать сметчик цикла
TESTS ROTL *1,R5,RS I Проверить бит
BL88 BIT.8ET
BIT.SETi
SOBGTR R6,TE8T
305
Пусть до начала выполнения цикла содержимое регистра R5 составляет:
^31 ^30 ^29 ••• ^2 *0
Какой бит проверяется первым? Какой бит проверяется последним? Если изме-
нить команду ROTL на
ROTL #-1, R5, R5
какой бит будет проверяться первым, а какой — последним?
13. Предположим, что необходимо запомнить три однобитовых флага. Какой
единицей памяти следует воспользоваться и куда поместить флаги? Почему? Дайте
ответы на эти же вопросы для четырех флагов.
14. В процедуре TURNOFF (рис. 12.4) циклически сдвигается копия аргумен-
та, представленного двоичной цепочкой. Измените процедуру, пользуясь командой
BIT и маской так, чтобы сдвигалась маска, а двоичная цепочка оставалась неиз-
менной. Какая из процедур лучше?
15. Если команда AOBLEQ в процедуре TURNOFF (рис. 12.4) изменена на
команду AOBLSS (и других изменений нет), то с какими аргументами, если они есть,
процедура будет выполняться неправильно? Может ли такая замена считаться улуч-
шением?
16. Напишите команды для подсчета установленных битов в длинном слове S.
17. Напишите процедуру нахождения позиции левого установленного бита в
длинном слове. Процедура должна иметь два аргумента: BIT__STRING и POSITION,
как и процедура TURNOFF (рис. 12.4).
18. Напишите процедуру SCORE, описанную в упр. 16 гл. 9.
19. Предположим, что данное в байте с адресом BYTE занимает биты 6:0.
Напишите команды установки или сброса бита 7 так, чтобы в байте было четное
число установленных битов. (Задачу можно решить громоздким и утомительным
способом, но есть и более короткий путь, основанный на применении команды
XORB.)
20. Напишите процедуру REVERSE, которая изменяет порядок следования битов
в длинном слове (аргументе) на противоположный. Если, например, аргумент имеет
вид
^31 ^30 ^29 ••• ^2 ^0»
то процедура REVERSE превращает его в длинное слово
^2 ••• ^29 ^30 ^31*
21. Напишите процедуру, которая преобразует символьный код в упакованный
десятичный формат, без использования команды CVTSP. Процедура должна иметь
три аргумента: LSN — цепочку символьного кода (т. е. в списке аргументов находит-
ся адрес байта, содержащего знак), NUM — число разрядов в цепочке и PKD—
место для* размещения результата выполнения процедуры.
Процедура должна возвращать в регистре R0 флаг, показывающий, было ли
преобразование успешным: единица означает успех, нуль — неудачу. (Причинами
неудачи могут быть недопустимый символ в символьной цепочке или неправильное
число десятичных разрядов.)
22. Предположим, что при выполнении команды
ASHL BITS, R7, R7
байт в BITS содержит целое число, меньшее —32. Если в регистре R7 хранилось
положительное целое число в формате длинного слова, что будет в этом регистре
после выполнения команды? Соответствует ли результат интерпретации арифмети-
ческого сдвига вправо как деления на степень 2? Дайте ответы на аналогичные
вопросы для случая, когда в регистре R7 было отрицательное целое число.
23. Можно ли с помощью команд в примере 12.12 обеспечивать требуемый
результат, если п = 0? (Зависит ли ответ от состояния флага разрешения целочис-
ленного переполнения?)
24. Каково будет содержимое регистра R8 после выполнения команд из пример^
12.12, если: а) п = 12, р = 2? б) п — 6, р = 0? в) п = 12, р = 25? г) п = 1, р= 17?
25. В примере 12.12 показано, как построить маску, состоящую из цепочки
единиц в поле регистра п нулей в других разрядах. Предположим, что нужно создать
альтернативный вариант маски: с цепочкой нулей и единиц в других разрядах,
например:
1...100000001...1
306
Какие изменения, если они необходимы, потребуется внести в пример 12.12
для построения таких масок? Рассмотрите изменения в самих командах, изменения
в. интерпретации программистом /г и р, а также исходное содержимое регистров
R6 и R7.
26. Переделайте упр. 11.4 (разработка макрокоманды CVTLQ), пользуясь
командой ASH. (Можно считать, что фактический аргумент QUAD является име-
нем.) Новое макроопределение должно быть короче разработанного ранее.
27. Пусть S и Т — длинные слова, представляющие собой множества, как
описано в разд. 12.4. Напишите команды для проверки SczT и перехода к SUBSET,
если условие удовлетворяется.
28. Предположим, что S — длинное слово, которое представляет собой под-
множество универсального множества, содержащее целые числа от 0 до 31. Напиши-
те команды для перехода к IN_SET, если целое число в регистре R8 является элемен-
том множества.
29. Допустим, вы имеете дело с множествами, содержащими до 256 элементов
(это максимальный размер множеств в языке Паскаль системы VAX). Каждое
множество представляется двоичной цепочкой, находящейся в восьми смежных
длинных словах памяти. Напишите макрокоманды UNION (объединение),
INTERSECTION (пересечение) и COMPLEMENT (дополнение), которые реализуют
соответствующие операции над множествами. Можете считать, что фактические
аргументы макрокоманд являются выражениями.
30. Пользуясь исходными данными из упр. 29, напишите макрокоманды
В_______SUBSET, В_EQUAL и В__EMPTY, аргументы и функции которых описываются
следующим образом:
Перейти по назначению, если
В__SUBSET множ 1, множ2, назначение множ!с:множ2
В__EQUAL множ!, множ2, назначение множ!=множ2
В__EMPTY множ, назначение множ=0
31. Процедура GETNUM (рис. 12.5), вызываемая процедурой SET_______UP,
возвращает в регистре R0 флаг, сигнализирующий о том, успешно ли найдено
и преобразовано число. К сожалению, однозначно причину неудачи флаг не пока-
зывает. Предположим, что процедура GETNUM модифицирована и возвращает
два флага. Как и ранее, бит 0 в регистре R0 сообщает, найдено ли и преобразовано
число. Если этот бит находится в состоянии 0, бит 7 в регистре R0 показывает,
что в строке нет больше чисел (бит 7 установлен) или что возникла какая-либо
ошибка (бит 7 сброшен). Перепишите процедуру SET_UP так, чтобы она возвращала
флаг в случае неудачи, если GETNUM встречает любой «плохой» вход.
32. Процедура SET_UP (рис. 12.5) с помощью двух команд ASHL и BISL2
устанавливает в представлении множества бит, который соответствует элементу
множества, хранимому в длинном слове NUMBER. Напишите команду INSV для
выполнения аналогичного действия.
33. Задачу формирования маски (пример 12.12) при задании ее начальной
позиции и длины можно решить, применив всего две команды. Напишите эти
команды. (Воспользуйтесь командой, рассмотренной в разд. 12.5.)
34. Процедура TURNOFF___2 (рис. 12.7) находит в длинном слове первый
включенный бит, выключает его и возвращает в вызывающую программу его
позицию. Напишите процедуру TURNOFF 3 с тремя аргументами BIT_STRING,
START___POSN и POSITION, где BIT____STRING и POSITION имеют тот же смысл,
что и ранее, а поиск первого установленного бита должен начинаться с позиции,
определяемой аргументом-байтом START___POSN. Поиск должен «закругляться»
к началу поля: если нет установленного бита в диапазоне 31:START_POSN, про-
цедура должна сканировать биты START_____________________________POSN—1:0.
35. Пользуясь описанием проблемы дисковой карты (пример 12.15), напишите
команды поиска доступного дискового блока, не применяя команды, реализующие
операции над двоичными полями.
36. В примере 12.15 в дисковой карте отыскивался сброшенный бит, показы-
вающий доступный дисковый блок. Предположим, что требуется найти группу
из п смежных блоков, где 1^и^32. Напишите команды поиска номера блока,
307
являющегося первым в группе из п доступных блоков, или перехода к NO__ROOM,
если такой группы блоков нет.
37. Напишите последовательность команд для проверки наличия в регистре
R10 точно одного установленного бита с переходом к метке ONE_______BIT, если
условие удовлетворяется. Попробуйте решить задачу, пользуясь (и не пользуясь)
командами, рассмотренными в разд. 12.5.
38. Пусть содержимое регистра R5 составляет FFFFFFFF. Определите его
содержимое после выполнения команды
EXTV #5, #0, ф'ХFFFFFFFF, R5
39. Подробно рассмотрите графику двоичных матриц из примера 12.17. На-
пишите процедуры для построения следующих изображений:
а) горизонтальной линии, если заданы номер строки, правая конечная точка
(как смещение в битах относительно правой стороны экрана) и длина (в битах);
б) вертикальной линии, если заданы номер нижней строки, номер верхней
строки и смещение относительно правой стороны экрана (в битах);
в) контура прямоугольника по спецификациям, приведенным в примере 12.17;
г) сплошного прямоугольника по спецификациям, приведенным в примере 12.17,
но при ширине, большей 32 элементов.
40. Для каждой из команд CMPV и CMPZV напишите заменяющую их после-
довательность команд, по которой можно выполнить такую же операцию.
41. Напишите команды для представления ответов участников викторины в
Формате длинного слова (пример 12.18), считая, что ответы хранятся в массиве
NSWERS длинных слов.
42. Напишите процедуру для преобразования цепочки (в символьном коде)
из восьми шестнадцатеричных разрядов в шестнадцатеричное целое число в формате
длинного слова. Если, например, первым аргументом является символьная цепочка
’’FFC03008”, процедура возвращает как второй аргумент длинное слово, содержащее
FFC03008. (Эта процедура удобна для преобразования входных данных программы,
предназначенной для проверки некоторых примеров разрядных команд, рассмотрен-
ных в настоящей главе.)
43. Предположим, что команды CALLG и CALLS отсутствуют, а есть только
команда CALL с одним аргументом (именем точки входа процедуры), которая
передает управление процедуре. Напишите команды, по которым реализуются все
операции, обычно выполняемые по команде CALLG: построение в стеке кадра
вызова и передачу новых значений в АР и FP. (Можно считать, что маска
входа не разрешает прерывания. Все необходимые для решения команды уже
рассмотрены, кроме команды
MOVPSL получатель
которая передает содержимое PSL в получатель.)
Глава 13
Числа,
представленные в форматах
с плавающей точкой
и упакованном десятичном
В настоящей главе рассматриваются два типа данных, применяю-
щиеся в больших ЭВМ (но обычно отсутствующие в малых ЭВМ):
числа с плавающей точкой и десятичные числа в упакованном
формате. Система VAX поддерживает оба эти типа данных на уровне
машинных команд, по которым выполняются арифметические и дру-
гие операции.
13.1. Представление чисел
в формате с плавающей точкой
Научная нотация. Для записи очень больших и очень малых
вещественных чисел обычно применяется «научная нотация» в сле-
дующем формате:
±т • ЮР,
где т — мантисса (1^/п^Ю), а р — целое число. Приведем
несколько примеров:
1.48Х103 - 3.0083 X 10м 4.87Х10-12
Диапазон мантиссы выбирается таким образом, чтобы в указанной
выше форме было представимо любое рациональное число.
Внутреннее представление чисел с плавающей точкой в ЭВМ
аналогично научной нотации. Оно содержит знак, мантиссу, кото-
рая обычно является правильной дробью (т. е. меньше единицы),
и степень (со знаком) основания системы счисления г (как правило,
2, 8 или 16, но не 10). Следовательно, числа с плавающей точкой
записываются в виде:
309
±fXrp, где
Значения f и р ограничены числом используемых для их хра-
нения битов, поэтому в ЭВМ невозможно представить все рацио-
нальные числа. Но и для тех чисел, которые можно записать в этом
формате, представление неоднозначно, например:
.1101 Х23 = .001101x2s
(здесь дроби показаны в двоичной системе счисления).
Числа с плавающей точкой. В системе VAX имеется два стан-
дартных типа (формата) чисел с плавающей точкой: одинарной
точности (тип F floating) и двойной точности (тип D____floating).
Кроме того, как необязательные допускаются типы G______floating и
Н__floating-, они служат для представления чисел с большими поряд-
ками и высокой точностью. В общем виде число е плавающей
точкой представляется следующим образом:
±fx2P
где 5</<1.
Число в формате типа F занимает длинное слово, числа в форма-
тах типов D и G — тетраслово, а число в формате типа Н — окта-
слово. Типы форматов различаются только числом битов, исполь-
зуемых для хранения порядка и мантиссы (дроби). Общие форматы
и способы представления всех трех компонентов (знак, мантисса
и порядок) одинаковы.
Введем обозначения: t — число битов порядка; и — число
битов в поле мантиссы. Число с плавающей точкой, представлено
в нормализованной форме, если первая цифра справа от точки отлич-
на от нуля. В системе VAX числа с плавающей точкой всегда
нормализованы: первый бит справа от точки равен единице, поэтому
/^.5ю. Нормализованное представление имеет два достоинства.
Так как первый бит равен единице, его не нужно включать в пред-
ставление, а можно предполагать. В биты мантиссы, реально фигу-
рирующие в представлении, не включается первый бит. Следователь-
но, представление с плавающей точкой, содержащее и битов ман-
тиссы, фактически имеет и+ 1 значащих битов. Второе достоинство
заключается в том, что представление чисел в нормализованной
форме увеличивает точность вычислений. Отметим, что нормализо-
ванное представление ненулевого числа в любом формате с плаваю-
щей точкой однозначно.
Формат одинарной точности F имеет вид:
31 16 15 14 7 6 0
S b.exp fi
Здесь S — знаковый бит, f = .lftf2 и b.exp—«смещенный порядок»=
= р+12810.
Поясним это представление. Местоположение знакового бита’
и наличие сегментов мантиссы нам кажется неудобным. Целесообраз-
но поменять местами обе половины представления, чтобы знак
зю
Число ±f х 2Р представляется следующим образом:
S Ь. ехр. *1
f2
Refloating Defloating
t = 8, u = 23 t« 8, u = 55
15 14 7 6 0 15 7 6 0
первое слово —
второе слово —
третье слово ~
четвертое слово —►
Refloating
t = 15, Ц=112
G_f bating
t = 11,u = 52
Рис. 13.1. Форматы чисел с плавающей точкой. Здесь t = число
разрядов порядка, и — число разрядов мантиссы, s = бит
знака,
•Vlfe
f=
(тип F),
(типы G и D),
Ahhhhhhh (тип Н),
Ь.ехр = «смещенный порядок» = р -|- 2 ' 1 (для 6,ехр>0).
Особые случаи: S=0 и />.ехр=О представляет 0.0, S=1 и
й.ехр=О — зарезервированный операнд
помещался в бите 31, а мантисса была непрерывной цепочкой битов.
Но формат с плавающей точкой в системе VAX выбран для обес-
печения его совместимости с форматами F и D в ЭВМ PDP-11,
в которых базовой единицей памяти является 16-битовое слово.
Представления чисел оказываются более естественными при рассмот-
рении последовательности слов, а не длинных слов. Все четыре
типа форматов показаны на рис. 13.1.
Что такое «смещенный порядок» и почему представление
F__floating содержит р+128, а не р? Для порядка чисел в системе
VAX отводится t бит (1 = 8 для форматов F и D, f=l 1 для формата
G и t = 15 для формата Н), в которых можно представить 2‘ значений.
311
Поскольку порядок числа может быть как положительным, так и от-
рицательным, в дополнительном коде допускается представление
от — 2‘~' до 2/_|—1 ( — 128 ... 127 для F и D), но здесь это не сделано.
Схемы, реализующие арифметические операции с плавающей точкой,
работают более эффективно, если фактические порядки соответству-
ют упорядочению их представлений, интерпретируемых как беззна-
ковые двоичные числа. В дополнительном коде из-за наличия знако-
вого бита отрицательные целые числа становятся «больше» поло-
жительных. В представлении, принятом (во многих ЭВМ) для по-
рядков, значения присваиваются двоичным наборам в поле порядка
в естественной последовательности. В системе VAX при t = 8 полу-
чаем:
Представляв-
Двоичный набор Десятичный эквивалент мый порядок
00000000 0 специальный случай
00000001 1 -127
00000010 2 -126
01111111 127 -1
10000000 128 0
10000001 129 1
11111111 255 127
Таким образом, смещенный порядок — это просто значение
двоичного набора как беззнакового двоичного числа. Он больше
представляемого им истинного порядка р на 2'_|. Следовательно,
«смещение» равно 2'-'. Смещенный порядок 0 не означает порядок
—2/_|, а считается «специальным случаем». Число с плавающей
точкой, у которого смещенный порядок равен 0 и знак (бит 15) равен
О, представляет нуль с плавающей точкой, т. е. число 0.0 (незави-
симо от значения мантиссы). Смещенный порядок 0 и знак 1 соот-
ветствуют зарезервированному операнду — недействительному чис-
лу; если оно оказывается операндом команды, реализующей операции
над числами с плавающей точкой, возникает ошибка зарезерви-
рованного операнда. В табл. 13.1 приведены диапазоны значений
и точность чисел, которые могут быть представлены в формате с
плавающей точкой в системе VAX.
В длинном слове (тетраслове, октаслове), представляющем чис-
ло с плавающей точкой, ничто не указывает на то, что оно является
числом с плавающей точкой, а не целым числом. CPU интерпре-
тирует двоичный набор как данное того типа, который соответствуй
ет команде, реализующей выполняемую над ним операцию.
Далее во всех нижеследующих примерах, если тип формата
специально не оговорен, подразумеваются числа с плавающей точкой
одинарной точности.
312
Таблица 13.1
Диапазоны чисел с плавающей точкой
Тип формата Порядок, бит Мантисса, бит Диапазон (приближ.) Число зна- чащих цифр (приближ.)
F 8 23 ,29Х lO-^.-UX 10“ 7
D 8 55 .29Х10-38... 1.7X10“ 16
G 11 52 .56Х1О-ЗО8....9Х 1030’ 15
Н 15 112 .84Х 10—4932... .59Х 104932 33
Пример 13.1. Интерпретация числа с плавающей точкой. Необходимо опре-
делить, какое число с плавающей точкой представляет длинное слово 0000С150.
Двоичный набор:
0000000000000000 1100000101010000
/г S b.exp fi
Число отрицательное, так как S = l. Смещенный порядок 10000010 равен
130|0, поэтому фактический порядок р = 2. Мантисса числа равна . lfj2, а само число
— .1101 х2*= —11.012 = —3.2510
(Напомним, что Лй разряд справа от точки имеет вес 2“"*.)
Отметим, что левый бит смещенного порядка показывает знак
порядка, так как его вес равен в двоичном числе 128 (или,
в общем случае, 2'-1). Смещенный порядок больше или равен
128(2'“') и р^0, если бит 14 установлен.
Пример 13.2. Преобразование в формат с плавающей точкой. Необходимо пре-
образовать десятичное число 5031.1875 в число с плавающей точкой.
5031.1875= 13А7.31в= 1001110100111.00112 = .10011101001110011 X 2‘3
fl fa
Так как р= 13, то смещенный порядок равен 141 или 10001101. Следовательно,
число в формате с плавающей точкой имеет вид:
00111001100000000100011010011101
Его шестнадцатеричный эквивалент составляет 3980469D.
Пример 13.3. Двойная точность. Чтобы показать размещение мантиссы числа
с плавающей точкой в формате типа D, рассмотрим следующее представление:
Первое 11111111111111110100000000000000
-длинное слово
Второе 11111111111111110000000000000000
длинное слово
Получается число:
.100000001111111111111111000000000000000011111111111111112
Инициализация памяти. Константы с плавающей точкой запи-
сываются в виде десятичных чисел. Допускается записывать их
со знаком и без знака, с указанием и без указания порядка (степень
10), с десятичной точкой и без нее (если число—целое). При исполь-
зовании десятичной точки слева от нее должна быть минимум одна
цифра.
Пример 13.4. Константы с плавающей точкой:
1.13 — 27.00056Е15 247 0.25 -5Е—8
! 1.24Е—3 = 0.124Е—2 = 124Е—5=0.00124 = 0.00000124ЕЗ
Память инициализируется по директивам:
.х__FLOATING список____________констант____с___плавающей_____точкой
313
Здесь x=F, D, G или H. Для директив F____________FLOATING и
D FLOATING допускаются более короткие мнемоники
.FLOAT и .DOUBLE соответственно. Ассемблер инициализирует
следующую доступную область памяти на значения заданных
констант.
Память для чисел с плавающей точкой можно резервировать
с помощью директив .BLKx (где x = F, D, G или Н), рассмотренных
в гл. 4.
Пример 13.5. Резервирование и инициализация памяти:
PIi -FLOAT 3.14159
RADIUSI .BLKF 1
AREA! . BLKF 1
13.2. Операции над числами
с плавающей точкой
В системе команд VAX предусмотрены команды для выполнения
арифметических операций над числами с плавающей точкой, про-
верок и сравнений и других подобных операций, а также для преоб-
разований чисел с плавающей точкой. Команды преобразований
имеются не во всех ЭВМ, но в системе VAX они есть. Кроме того,
существует несколько специальных .мощных команд, реализующих
сложные операции над числами с плавающей точкой (например,
POLY и EMOD), которые в настоящей книге не рассматриваются.
Стандартная система команд ориентирована на форматы типов
F и D. Необязательная система команд для форматов G и Н
позволяет выполнять те же операции. (Коды операций таких команд
имеют длину 2 байта.)
Операнды с плавающей точкой могут находиться в памяти, в
регистрах или в самой команде как литеральные или непосредствен-
ные операнды. Если в команде, реализующей операции над числа-
ми двойной точности, определяется регистр, например Rn, исполь-
зуются Rn и Rn+ 1; Rn содержит первое длинное слово операнда
(со знаком, смещенным порядком и старшими битами мантиссы),
a Rn+1 —дополнительные биты мантиссы.
Арифметические и другие аналогичные им команды. Базовыми
арифметическими командами являются следующие:
(add) (т)
sub
MUL
DIV
D
G
н
(2 onl,on2
[3 onl, on2, получатель
Назначения операндов удовлетворяют тем же соглашениям,
что и в командах целочисленной арифметики. Например, в команде
SUBF3 первый операнд вычитается из второго, и результат помеща-
ется в третий операнд. Все команды воздействуют на коды условий:
N = 1, если результат меньше 0 (или оказывается зарезервированным операндом);
Z = 1, если результат равен 0.0;
V= 1, если результат вызывает переполнение или делитель в команде DIV равен 0.0;
С = 0.
314
Особые случаи возникают при переполнении (когда порядок
результата больше 127 для форматов F и D и больше 2'°—1 или
214—1 для форматов G и Н), в ситуациях с зарезервированными
операндами и при делении на нуль. Программист разрешает или
запрещает особый случай исчезновения порядка (если он меньше
— 127), воздействуя на шестой бит RSW — бит разрешения прерыва-
ния при исчезновении порядка в операциях над числами с плаваю-
щей точкой. При исчезновении порядка в операнде-получателе
запоминается число с плавающей точкой, равное 0.0.
Напомним, что 0.0 имеет множество представлений. Любое
число с нулями в полях знака и смещенного порядка считается
нулем независимо от содержимого битов мантиссы. Однако, если
результат выполнения операции над числами с плавающей точкой
равен нулю, CPU всегда запоминает представление с нулями во
всех битах мантиссы. Арифметические операции над числами с
плавающей точкой будут рассмотрены более подробно после описа-
ния нескольких специальных команд.
Для выполнения операций над числами с плавающей точкой
предусмотрены следующие специальные команды:
CLRx получатель
MOVx источник» получатель
MOV Ах источник» получатель
MNEGx источник, получатель
где x=F, D, G или Н. Команды CLRx и MOV Ах с x=F или D
инициируют точно такие же операции, как команды CLRx и MOV Ах
с x = L или Q. По существу, это просто другие мнемоники соот-
ветствующих команд, реализующих операции над целыми числами;
коды операции в них одинаковы. Команды MOV для чисел с пла-
вающей точкой и целых чисел не идентичны, так как они обеспе-
чивают разные проверки для установки кодов условий и для опре-
деления ошибки. По командам MNEG в получатель пересылается
копия операнда-источника с инвертированным знаком. (Если значе-
ние источника равно нулю, знак не изменяется.) Эти команды
.воздействуют на коды условий N и Z обычным образом и сбрасы-
вают флаги V и С. Команды 1NC и DEC для чисел с плавающей 'точ-
кой отсутствуют.
Команды проверки и сравнения имеют форматы:
TSTx операнд
СМРх onl, оп2
где х= F, D, G или Н. Они удовлетворяют стандартным соглашениям.
В системе VAX разработан полный набор команд CVT для
Преобразований чисел с плавающей точкой и целых чисел, включая
и два варианта преобразования чисел с плавающей точкой в длин-
ные слова (с округлением и усечением). Формат таких команд:
СУТху источник, получатель
315
где х и у могут быть равны F, D, G, Н, В, W или L, но х=£у,
xy#=DG и xy^=GD. Команды преобразования с округлением имеют
вид:
CVTRxL источник, получатель
vjtfi x = F, D, G или H. Команда CVTxL вместо округления производит
усечение операнда с плавающей точкой до целого числа. Если преоб-
разованный результат не представим в получателе, устанавливается
код условия V.
Пример 13.6. Преобразования. Предположим, что переменная ALPHA опреде-
лена директивой:
ALPHA: .FLOAT —15.9
Содержимое длинного слова в ALPHA составляет 6666С27Е. Ниже показаны ре-
зультаты выполнения нескольких команд преобразования:
Команда Реоультат R7
CVTFW ALPHA,R7 xxxxFFFl
CVTFL ALPHA,R7 * FFFFFFF1
CVTRFL ALPHA,R7 FFFFFFFO
CVTFD ALPHA,R7 6666C27E R7
ОООООООО в R8
(Символы х показывают неизменяющиеся разряды R7.)
Большинство команд CVT инициируют довольно сложные опера-
ции, которые в большинстве ЭВМ необходимо программировать
в виде последовательностей команд, включая в них несколько
разрядных операций. Поэтому целесообразно обсудить вопрос о том,
как выполнялись бы преобразования при отсутствии команд CVT.
В разд. 13.5 показана процедура преобразования числа с плавающей
точкой в длинное слово без использования команды CVTFL.
Несколько преобразований рассмотрены в упражнениях.
Преобразование формата с плавающей точкой в символьный
код для вывода результатов оказывается еще более сложной проб-
лемой и даже в системе VAX она не разрешима с помощью одной или
двух команд. Однако в библиотеке процедур VAX-II имеются про-
цедуры преобразования, которыми пользуются компиляторы языков
высокого уровня и которые может непосредственно вызывать прог-
раммист, работающий на языке Ассемблера.
Команды управления циклом АСВ (см. гл. 7) допускают пара-
метры цикла в формате с плавающей точкой. Напомним формат
команды АСВ (сложить, сравнить и перейти):
АСВх предел, инкремент, индекс, назначение
где x = F, D, G или Н (в добавление к типам В, W или L).
Команды АСВ являются наиболее гибкими среди команд управле-
ния циклом, так как они дают возможность программисту определять
инкремент цикла, который может быть положительным или отрица-
тельным, и назначение кодируется смещением размером в слово, а
не в байт.
316
Пример 13.7. Параметры цикла в формате с плавающей точкой. Цикл for языка
Алгол:
for х: = 2.8 step—.2 until 1.0 do
<тело цикла >
можно реализовать следующим образом:
; ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ! R6 1 индекс цикла 1 Инициализировать индекс
LOOP! MOVF #2.8,R6
Стекло ACBF цикла> #1.О,#-О.2,R6fLOOP ; Управление циклом
Выполнение арифметических операций. Чтобы показать, как
выполняются арифметические операции над числами с плавающей
точкой, рассмотрим сложение и вычитание. Некоторые из приведен-
ных ниже положений легко применимы к умножению и делению.
Предположим, что необходимо сложить числа с плавающей точ-
кой fiX2P| и ^Х2₽2. Просто сложить мантиссы нельзя, если порядки
Pi и р2 не равны. Поэтому сложение выполняется следующими
действиями:
I. Найти п= | pi—р2 |.
2. Сдвинуть мантиссу числа с меньшим порядком вправо на п
разрядов. (Теперь оба порядка равны' большему из чисел pt и
Р2)
3. Сложить мантиссы.
4. При необходимости нормализовать результат путем сдвига
мантиссы и коррекции порядка.
Прежде чем перейти к детальному рассмотрению этой операции,
приведем простой пример.
Пример 13.8. Сложение. Пусть содержимое регистра R6 составляет 6000580С,
а регистра R7—90005425. Нужно определить содержимое регистра R8 после выпол-
нения команды
ADDF3 R6, R7, R8
Декодируя представления чисел, получаем следующие условия задачи:
(R6) ’.8С6000Х248
(R7) + .А59ОООХ240
Для удобства мантиссы чисел оставлены шестнадцатеричными, а порядки
показаны как десятичные числа. Второй операнд необходимо сдвинуть на восемь
двоичных разрядов (или на два шестнадцатеричных);
.8С6000Х248
+ .00А590Х248
Сложение мантисс дает следующий результат:
.8 0590Х248
Сумма получилась нормализованной, так первый бит цифры 8 равен единице.
Содержимым регистра 8 будет 0590580D.
317
Когда при выравнивании порядков один из операндов сдвигается
вправо, его некоторые ненулевые биты могут теряться. Для обеспе-
чения большей точности вычислений часть этих битов необходимо
учитывать. Способ вычислений, принятый в системе VAX, экви-
валентен введению в мантиссах справа двух дополнительных битов,
называемых защитными. Кроме того, предусмотрен дополнительный
бит слева от точки для фиксации переноса из старшего разряда
мантиссы. Напомним, что фактическое число битов в поле мантиссы
числа с плавающей точкой равно и; старший бит мантиссы не
запоминается. После нормализации («+1)-битовая мантисса
округляется: если первый защитный бит равен 1, то в ее младший
разряд прибавляется единица. Получается точный результат, округ-
ленный до и+1 бит. Следовательно, максимальная ошибка равна
половине значения младшего бита мантиссы. (Хотя эта ошибка
очень мала, ниже будет показано, что она может иметь значитель-
ное влияние на результаты вычислений).
Пример 13.9. Сложеннее округлением результата. Предположим, что (ALPHA) =
= 739E448D и (ВЕТА) =94704722. Требуется определить содержимое GAMMA после
выполнения команды
ADDF3 ALPHA, BETA, GAMMA
Декодируя представления, получаем:
(ALPHA) .100011010111001110011110Х29
+
(ВЕТА) .101000101001010001111101Х2И
Первый операнд сдвигается вправо на пять разрядов. Три бита справа пропа-
дают, а два становятся защитными:
Защитные биты
(ALPHA) после сдвига .00000100011010111001110011 X 2й
+
(ВЕТА) .101000101001010001111101Х2И
Сложение мантисс дает результат:
0.10100111000000000001100Ш X 214
Сумма уже нормализована. Так как первый защитный бит равен единице,
результат округляется следующим образом:
.101001110000000000011001 Х214
+ _________________________1
.101001110000000000011010 Х214
Содержимое GAMMA составит 001А4727.
Когда же используется второй защитный бит? Если после выпол-
нения операции требуется сдвиг результата влево на один разряд
для нормализации, второй защитный бит применяется для округления
(см. следующий пример). Конечно, может оказаться, что нормализа-'
ция потребует сдвига мантиссы влево на несколько разрядов (на-
пример, при вычитании близких по величине положительных чисел).
Тогда справа в мантиссу «вдвигаются» оба защитных бита и допол-
нительные нули.
Пример 13.10. Вычитание с нормализацией результата. Пусть содержимое
регистра R7 составляет 00004000 (т. е. 1/2), а регистра R9—00013F00 (1/8+2""26).
Нужно определить содержание регистра R7 после выполнения команды i.:
SUBF2 R9, R7
318
Задача сводится к вычитанию (подчеркнуты защитные биты):
24 бита
0.10... 0000 Х2°
0.10... 0100 Х2-2
Вначале представим вычитаемое в дополнительном коде. Отметим, что необ-
ходимо явно показать бит переноса и два защитных бита, так как они участвуют
в образовании дополнительного кода второго операнда.
0.10... 0000 Х2°
4-
1.01 ... 1100 Х2-2
Сдвиг второго операнда (причем бит переноса считается знаковым битом
и расширяется) и сложение дают:
0.10 ... 000000 Х2°
4-
1.1101 ... 11П Х2°
0.0101 ... 1Ш Х2°
Сумма нормализуется посредством сдвига влево на один разряд:
0.101 ... 1 ИГО Х2-*
Для округления результата первый защитный бит (который в сумме был
вторым защитным битом) прибавляется к 24-битовой мантиссе:
0.110 ... 000 Х2—1
Полученное число равно правильному округленному результату 3/8.
Оно загружается в регистр R7 как 00003FC0.
Нарушения правил. Результат вычитания в примере 13.10 (без
округления) оказался неточным. Поскольку для представления
мантиссы числа с плавающей точкой используется конечное число
битов, точные результаты получаются не всегда. По этой же причине
арифметические операции над числами с плавающей точкой в ЭВМ
не удовлетворяют стандартным правилам арифметики. Рассмотрим
следующее правило:
если a+b—b, то а = 0.
Предположим, что складываются числа 1/2 и 224, которые
легко представляются в формате с плавающей точкой. Сложение
выполняется следующим образом:
.1X2°
4-
.1X25*
После сдвига первой мантиссы получаем (защитные биты подчер-
кнуты) :
.00000000000000000000000001 Х225
+ —
. ЮООООООООООООООООООООООООХ 225
. 10000000000000000000000001X 2“
Первый защитный бит содержит нуль, поэтому они оба отбрасы-
ваются. Результат равен Ь, но а=1/2#=0. Очевидно, ошибка
319
здесь возникает потому, что слагаемые значительно различаются
по величине. Это общая проблема при выполнении арифметических
операций под числами с плавающей точкой. Для повышения точ-
ности результатов вычисление длинных выражений следует организо-
вать так, чтобы первыми в них участвовали числа с близкими значе-
ниями. Это положение иллюстрируется примером в разд. 13.4.
Читателю предлагается самостоятельно найти пример, показы-
вающий, что ассоциативный закон сложения
(а+6) + с=а+(Ь + с)
не всегда справедлив для чисел с плавающей точкой.
13.3. Непосредственные
и литеральные операнды
с плавающей точкой
В машинных командах константы с плавающей точкой допус-
кается определять как непосредственные или литеральные операнды.
По аналогии с целыми числами константе предшествует знак #,
например:
MULF3 #0.33333333, R6, AREA
MOVF #40, R7
Спецификатор первого операнда в команде MULF3 будет ассемб-
лироваться в непосредственном режиме. Ассемблер подставит
представление 0.33333333 с плавающей точкой в длинном слове
следующим образом:
... 56 АAAB3FAA_ 8F 45
о.зззЪззз
Здесь 8F — байт режима, 45 — код операции.
При использовании формата типа D ассемблер построил бы
константу в тетраслове:
... 56 QOOOOOOOAAAB3FAA 8F 65 .
0.33333333
где 65 — код операции (MULD3).
Отметим, что ассемблер в этом случае заполняет младшие биты
мантиссы непосредственного операнда нулями (даже если в констан-
те записано 16 цифр). Для получения значащих битов мантиссы
числа в формате двойной точности константа должна быть опреде-
лена в директиве' .DOUBLE.
Операнды в литеральном режиме применялись для небольших
целочисленных констант в целях экономии памяти (см. гл. 8).
Левые два бита байта режима — нулевые (они показывают лите-
320
7 6 5
3 2
о о
Порядок
(положительный)
Мантисса
(старший бит опущен)
Рис. 1*3.2. Литерал в формате
с плавающей точкой
ральный режим), а осталь
ные шесть содержат кон-
станту. Ассемблер пред-
ставляет малые константы
с плавающей точкой в ли-
теральном режиме вместо
непосредственного режи-
ма также с целью эконо-
мии памяти. Литерал в
формате с плавающей точкой показан на рис. 13.2. В трех из шести
битов указывается порядок (истинный, а не смещенный) числа, а в
трех остальных — его мантисса. Знакового бита нет, поэтому в лите-
ральном режиме можно закодировать' только положительные числа.
Как и в обычном представлении с плавающей точкой, старший бит
мантиссы считается равным единице и опускается.
Поскольку число 40 можно закодировать как литерал в формате
с плавающей точкой, ассемблер в приведенной выше команде
MOVF принимает литеральный режим. Число 40=1010002=
—— 1 Л 1 О® П Л в а ч » п Ч'Т’ЛГЧ О г» плпли ЛЛ 1 '1 fl fl 1 fl NA Л ittll llltl I II r/л П ли nl I
= . 101X26,поэтому литерал равен 00110010. Машинный код команды
MOVF:
литерал код операции
57 32 50
Каким образом при выполнении команды CPU распознает
литерал как целое число или как число с плавающей точкой? Байт
литерала не содержит ничего, что позволило бы различить эти
два типа чисел. CPU интерпретирует тип литерала в соответствии
с выполняемой командой.
Пример 13.11. Интерпретация операндов литерального режима. Рассмотрим ма-
шинные коды:
первый байт первый байт
59 03 С2 и 59 03 42
Первой командой является SUBL2, а второй — SUBF2. Первым операндом в
обеих командах служит байт литерального режима 03. В команде SUBL2 процессор
интерпретирует литерал как целое число#3. В команде же SUBF2 литерал интер-
претируется следующим образом:
00000011 = . 1011 X 2° = .6875
Р fr____t
По первой команде CPU выполняет целочисленное вычитание (вычитает 3
из содержимого регистра R9, которое будет интерпретироваться как целое число
в формате длинного слова), а по второй—вычитание чисел с плавающей точкой
(вычитает .6875 из содержимого регистра R9, которое будет интерпретироваться
как число с плавающей точкой).
Если команда содержит литеральный операнд с плавающей точ-
кой, CPU до ее выполнения расширяет литерал на полный формат
(в зависимости от типа команды). На рис. 13.3 показано, как
осуществляется расширение прй использовании формата типа F.
В форматах D, G и Н дополнительные длинные слова содержат
нули.
11 Зак. 821 321
Литеральный операнд
Рис. 13.3. Расширение литерала с плавающей точкой
13.4. Точность вычислений
при расчете дисперсии (пример)
В разд. 13.2 показано, что. результаты выполнения арифмети-
ческих операций над числами с плавающей точкой не всегда оказы-
ваются точными, и рекомендовано при вычислениях длинных выра-
жений вначале обрабатывать числа, близкие по своим значениям.
Здесь мы обсудим, как могут различаться результаты при вычисле-
нии одной и той же величины по двум различным, но математи-
чески эквивалентным формулам. Объяснение причин, по которым две
формулы дают различные результаты, и методы оценки Точности вы-
числений можно найти в учебнике по математическому анализу.
Рассмотрим следующий пример. Предположим, что имеется мас-
сив из п чисел с плавающей точкой Х(1),.... Х(п), которые мы будем
называть выборками (или точками). Среднее (или среднее значение)
определяется формулой
Л
Среднее =
(=1
Стандартное отклонение является мерой того, как далеко нахо-
дятся выборки от среднего. Квадрат стандартного отклонения
называется дисперсией. В этом примере вычисляется дисперсия
множества выборок. Дисперсия определяется соотношением:
Л
var среднее]2. (13.1)
(=1
Обычно эта формула упрощается:
л
уаг = ^~У Х(1)2 — среднее2- (13.2)
/=1
322
.PSECT VARIANCE.l
; ПРОЦЕДУРА VARNC1 (SAMPLE, N, MEAN, VAR>
$
; Эта процедура вычисляет среднее и дисперсию массива SAMPLE чисел
; с плавающей точкой. В ней испольоуется Формула для дисперсии!
3
| 1/N ♦ SUM С(SAMPLE(i> - MEAN)*23
3
j ВХОДНЫЕ АРГУМЕНТЫ
; SAMPLE отсчеты (массив чисел с плавающей точкой)
; N число отсчетов (длинное слово)
3
; ВЫХОДНЫЕ АРГУМЕНТЫ
3 MEAN среднее отсчетных точек (число с плавающей точкой)
; VAR дисперсия (число с плавающей точкой)
3
; СМЕЩЕНИЯ ДЛЯ СПИСКА АРГУМЕНТОВ
3
SAMPLE - 4
N - 8
MEAN - 12
VAR 16
; ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ! ; 1 5 1 ; 1 1 1 R5 укаоатель массива R6 N, число точек R7 предел цикла (адрес последнего отсче* R8 рабочий R9 сумма отсчетов и среднее R10 дисперсия
3 .ENTRY VARNC1,*M<R5,R6,R7,R8,R9,R10>
MOVL SAMPLE(AP),R5 ; Адрес массива
MOVL 8N(AP),R6 3 N
SUBL3 #1,R6,R8 3 N-1
MULL2 #4,R8 ; 4*(N—1)
ADDL3 R5,R8,R7 у Предел цикла
323
CVTLF R6,R6 N в форме с плавающей точкой
I
CLRF R9 Сбросить под.сумму
ADDs ADDF2 (RS),R9 i Прибавить значение отсчета
ACBL R7,«4,RS,ADD Управление циклом
DIVF2 R6,R9 Вычислить среднее
MOVF R9,8MEAN(AP) 5 Запомнить сроднее
CLRF RIO ; Сбросить для суммирования
MOVL SAMPLE(AP) 5 Адрес массива
TERM: SUBF3 R9,(RS),R8 1 SAMPLE (1) - MEAN
MULF2 R8,R8 ; (SAMPLE(1) - МЕАЫ)Л2
ADDF2 R8,R10 ? Прибавить квадрат
1 ACBL R7,#4,RS,TERM Управление циклом
DXVF3 R6,R10,8VAR(AP) j Дисперсия
RET • END i Возврат
Рис. 13.4. Вычисление дисперсии по ф-ле (13.1)
PSECT VARIANCE.2
I
| ПРОЦЕДУРА VARNC2 (SAMPLE, N, MEAN, VAR)
5
; Эта процедура вычисляет среднее и дисперсию массива SAMPLE чисел
I с плавающей точкой. В ней используется формула для дисперсии:
I
; 1/N # SUMCSAMPLECi)Л2Э - МЕАЫЛ2
I
| ВХОДНЫЕ АРГУМЕНТЫ
I
; SAMPLE отчеты (массив чисел с плавающей точкой)
I N число отсчетов (длинное слово)
I
; ВЫХОДНЫЕ АРГУМЕНТЫ
I
I MEAN среднее отсчетных точек (число с плавающей точкой)
; VAR дисперсия (число с плавающей точкой)
I
| СМЕЩЕНИЯ ДЛЯ СПИСКА АРГУМЕНТОВ
324
I
SAMPLE = 4
N » 8
MEAN “ 12
VAR = 16
; ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ! R5 указатель массива
I R6 N, число точек
5 R7 предел цикла (адрес последнего отсчета)
1 R8 рабочий
1 R9 сумма отсчетов
? R1O сумма квадратов отсчетов
. ENTRY VARNC2, Z4M<R5,R6,R7,R8, R9,R10>
MOVL SAMPLE(AP),RS 1 Адрес массива
MOVL BN(AP),R6 ? N
SUBL3 #1,R6.,R8 I N-l
MULL2 #4,R8 I 4*(N-l)
ADDL3 R5,R8,R7 J Предел цикла
5
CLRF R9 1 Сбросить под сумму
CLRF RIO 1 Сбросить под сумму квадратов
?
ADD: ADDF2 ‘ (R5) ,R9 Прибавить значение отсчета
MULF3 (R5),(R5),R8 1 Квадрат отсчета
ADDF2 R8,R10 ? Прибавить квадрат
ACBL R7,#4,R5,ADD 1 Управление циклом
?
CVTLF R6,R6 J N в Форме с плавающей точкой
DIVF2 R6,R9 1 Вычислить среднее
MOVF R9,BMEAN(AP) ? Запомнить среднее
DIVF2 R6,R10 Усреднить сумму квадратов
MULF2 R9,R9 J Среднее в квадрате
SUBF3 R9,R10,BVAR(AP) I Дисперсия
? RET ? Возврат
.END
Рис. 13.5. Вычисление дисперсии по ф-ле (13.2)
325
На рис. 13.4 и 13.5 приведены две процедуры расчета дисперсии
по каждой из этих формул. Расчет по ф-ле (13.1) предполагает
наличие двух циклов: в одном цикле вычисляется среднее, а в дру-
гом—затем осуществляется суммирование. При расчете по ф-ле
(13.2) можно использовать один цикл для вычисления суммы
выборок и суммы их квадратов. Следует ожидать, что процедура,
реализующая ф-лу (13.2), окажется несколько проще и будет выпол-
няться быстрее, но, к сожалению, результат может быть неточным.
Вычисления и суммирование разностей между каждой выборкой
и средним по ф-ле (13.1) даст более точный результат, чем вычисле-
ние отдельных сумм, а затем их разности.
Для иллюстрации различия между формулами обе процедуры
были выполнены с несколькими множествами из п выборок, где
п = 100. В первой группе тестов выборки формировались из нормаль-
ных распределений с ожидаемым средним .1234567. и ожидаемыми
дисперсиями .1, .01, .001 и .0001. Во второй группе тестов половина
выборок формировалась как среднее *(1+е), а другая половина—
хак среднее *(1 —е), где е = .1, .01, .001 и .0001. Из ф-лы (13.1) оче-
видно, что правильное значение дисперсии для этой группы равно
(среднее *е) . В третьей группе тестов использовались равномерные
распределения с промежутками между точками, равными 4*е/п,
при прежних значениях е. Для второй и третьей групп среднее
составляло .1234567. Результаты выполнения тестов приведены на
рис. 13.6. (Ожидаемые среднее и дисперсия в случае нормальных
распределений являются единственными оценками.) Как видно из
рисунка, для первых трех тестов в каждой группе результаты
расчетов по обеим формулам аналогичны, но при е=.0001 ф-ла (13.1)
дает точный результат, а ф-ла (13.2)—нет.
Результаты, показанные на рис. 13.6, получены с помощью
основной программы, написанной на Фортране. Поскольку компиля-
тор Фортрана в системе VAX рассчитан на стандартные соглашения
о связи процедур, рассмотренные в гл. 9, основная программа может
вызывать ассемблерные процедуры без каких-либо дополнительных
требований. Операторы вызова процедур, представленных на рис.
13.4 и 13.5, имеют вид:
CALL VARNCI (SAMPLE, N, XMEAN, VARI)
CALL VARNC2 (SAMPLE, N, XMEAN, VAR2)
Одно из достоинств Фортран-программы заключается в том,
что в ней можно использовать стандартные операторы ввода-
вывода Фортрана для решения сложной задачи преобразования
чисел с плавающей точкой в символьные цепочки, которые появля-
ются при выводе.
Таким образом, независимо от того, написана ли процедура
на языке Ассемблера или на языке высокого уровня, при выполнении
операций над числами с плавающей точкой необходимо тщательно
следить за получением точных результатов.
326
НОРМАЛЬНОЕ РАСПРЕДЕЛЕНИЕ
СТАНДАРТНОЕ ОЖИДАЕМАЯ
ОТКЛОНЕНИЕ СРЕДНЕЕ ДИСПЕРСИЯ 1 ДИСПЕРСИЯ 2 ДИСПЕРСИЯ
О.1ООООООЕ+ОО О.1329546Е+00 О.894В1ВВЕ-02 О.8948196Е-02 О.ЮОООООЕ-О 1
О.1ООООООЕ-О1 О.1244065Е+00 О.894В187Е-04 О.894-334Е-04 О. ЮОООООЕ-ОЗ
О.1000000Е-02 О.1235517Е+00 О.В94В1В0Е-06 О.В912757Е-06 О. ЮОООООЕ-ОЗ
О.ЮОгОООЕ-ОЗ О.1234662Е+00 О.В94В234Е-08 О.9313226Е-09 0.9999999Е-08
ДВЕ ТОЧКИ
СТАНДАРТНОЕ ОЖИДАЕМАЯ
ОТКЛОНЕНИЕ СРЕДНЕЕ ДИСПЕРСИЯ 1 ДИСПЕРСИЯ 2 ДИСПЕРСИЯ
О. ЮОООООЕ+ОО О.1234565Е+00 О.1524156Е-03 О.1524547Е-03 О.1524156Е-03
О. ЮОООООЕ-О 1 О.1234566Е+00 О.1524159Е-05 О.1532957Е-05 О.1524156Е-05
О.1ООООООЕ-О2 О.1234566Е+00 О.1524234Е-07 О.4656613Е-07 О.1524156Е-07
О.ЮОООООЕ-ОЗ О.1234568Е+00 О.1524199Е-09 -О.1210719Е-07 О.1524156Е-09
РАВНОМЕРНОЕ РАСПРЕДЕЛЕНИЕ
СТАНДАРТНОЕ ОЖИДАЕМАЯ
ОТКЛОНЕНИЕ СРЕДНЕЕ ДИСПЕРСИЯ 1 ДИСПЕРСИЯ 2 ДИСПЕРСИЯ
О. ЮОООООЕ+ОО О.1234566Е+00 О.1293599Е-01 О.1293598Е-01 О.12936ООЕ-01
О.ЮОООООЕ-О1 О.123456ВЕ+00 О.1293617Е-03 О.1293560Е-03 О.1293600Е-03
О.ЮОООООЕ-02 О.1234567Е+00 О.1293722Е-05 О.1295470Е-05 О.1293600Е-05
О.ЮОООООЕ-ОЗ О.1234567Е+00 О.1294222Е-07 О.1583248Е-07 О.1293600Е-07
Рис. 13.6. Результаты расчетов дисперсии
13.5. Преобразование числа
с плавающей точкой*
в целое число (пример)
Рассмотрим пример преобразования числа х в формате с
плавающей точкой (тип F) в целое число п , представленное
дополнительным кодом в длинном слове, и приведем процедуру-
функцию, реализующую ту же операцию, что и команда CVTFL.
Кроме собственно, преобразования, процедура устанавливает коды
условий в слове PSW вызывающей программы, которое запоми-
нается в стековом кадре. Следовательно, при возврате из процеду-
ры коды условий имеют такие же значения, как если бы использова-
лась команда CVTFL.
Основные действия по преобразованию перечислены ниже и пока-
заны на рис. 13.7. Напомним, что процедура-функция возвращает
результат в регистре R0.
1. Выделить смещенный. порядок из разрядов 14:7 числа х в
рабочий регистр.
2. Если порядок х не положительный, то | х |<1 и п=0. (Порядок
не положительный, если смещенный порядок ^128). При л=0 уста-
новить бит Z в слове PSW вызывающей программы (которое
хранится в стеке в кадре вызова). Далее предполагается, что
порядок х положительный.
327
31
14
7
О
Рис. 13.7. Некоторые этапы преобразования числа с плавающей точкой (в фор-
мате типа F) в целое число
3. Выделить поле мантиссы (биты 31:16 и 6:0) в регистр R0.
4. Установить бит 7 регистра R0, восстановив тем самым стар-
ший бит мантиссы, который опущен в представлении с плавающей
точкой.
5. Поменять местами обе половины регистра R0, чтобы упорядо-
чить биты мантиссы.
6. Определить, на сколько разрядов необходимо сдвинуть
биты мантиссы. Заметим, что, если интерпретировать содержимое
R0 как целое число, его значением будет lfj2 или .1/J2 X 224.
Правильное же значение n = .lflf2x2p, поэтому R0 необходимо
сдвинуть (не циклически) на р—24 разряда. Если число р—24 явля-
ется отрицательным, сдвиг осуществляется вправо, как и должно
быть. Если сдвиг вызывает переполнение, установить .бит V в PSW
вызывающей программы.
7. Проверить знаковый бит (бит 15) числа х; при отрицательном
х изменить знак содержимого R0 и установить бит N в PSW вы-
зывающей программы.
Процедура преобразования приведена на рис. 13.8. Преобразова-
ние длинного слова в числе с плавающей точкой (без применения
команды CVTFL) рассматривается в упражнениях.
.PSECT CVTFL
I
I ФУНКЦИЯ CVTFL (ARG)
I
I Эта функция преобразует свой аргумент (число одинарной точности
328
с плавающей тонкой) в целое число (длинное слово) и возвращает
результат в R0. Преобразование усекает дробйую часть аргумента.
Коды условия в запомненном P8W вызывающей программы фиксируют
отрицательный и нулевой результат, а также переполнение.
.ENTRY CVTFL,AM<R2,R3>
| ИСПОЛЬЗОВАНИЕ 1 1 1 РЕГИСТРОВ! RO результат (длинное слово) R1 порядок R2 аргумент с плавающей точкой R3 счетчик сдвигов
1 SIGNl DONEi MOVF BICL3 ROTL CMPW В LEO BICL3 BISB2 ROTL 8UBL3 ASHL BVC BI8B2 T8TF BGEQ MNEGL BI8B2 RET 84(AP),R2 | Взять аргумент #*XFFFFflO7F,R2,Rl । Выделить смещенный порядок #-7,Rl,Rl | Порядок на месте Rl,*128 | Проверить, порядок > О ZERO | Порядок < О', результат 0 #*4X0000FF80,R2,R0 | Биты мантиссы в RO #xflO,RO । Ввести первый бит мантиссы #16,R0,R0 । Биты мантиссы * смежные #152,R1,R3 । Определить счетчик для сдвига R3,R0,R0 | Сдвинуть мантиссу SIGN । Перейти,если нет переполнения •*XO2,4(FP) | Установить флаг V в PSW R2 | Проверить знак DONE * Если неотрицательный, сделано RO,RO | Изменить знак •AX08,4(FP) | Установить флаг N в P8W у Возврат
1 ZEROi CLRL BI8B2 RET .END R0 । Результат 0, .если модуль аргумента<1 t*X04,4(FP) । Установить флаг Z в PSW 1 Возврат Рис. 13.8. Процедура CVTFL
13.6. Представление чисел
в упакованном десятичном формате
Мы уже имели дело с числами в упакованном десятичном форма-
те на промежуточном этапе преобразования дополнительного кода
в символьный (см. разд. 6.5). Целое число представляется цепочкой
десятичных цифр от 0 до 31, упакованных по две в байте, со знаком,
занимающим младшую тетраду последнего байта в цепочке
Первый байт
d d d d d d d s
Знак
Старшая Младшая
цифра цифра
Рис. 13.9. Упакованный .десятичный формат.
Знаки: А, С,. Е, F представляют «+», В, D —
«—». Число может содержать от 0 до 31 цифры
329
(рис. 13.9). Первый байт (с наименьшим адресом) содержит
старшую цифру, а остальные цифры следуют в естественном порядке.
(Ранее мы показывали содержимое памяти «наоборот», т. е. распо-
лагали байт с наименьшим адресом на диаграммах справа; в
естественном же формате упакованные десятичные числа следуют
слева направо.) Цифры представляются в двоичном коде, а знак—
двоичными наборами 1010—1111 (или шестнадцатеричными A—F).
Цифры А, С, Е и F обозначают плюс, а С и D — минус.
Знаки в десятичных цепочках, генерируемых CPU, представляются
как С( + ) и D( —). Примеры упакованных десятичных чисел были
приведены в разд. 6.5.
В VAX и многих больших ЭВМ предусмотрены машинные коман-
ды для выполнения арифметических операций над упакованными
десятичными цепочками. Поскольку уже созданы компактное пред-
ставление целых чисел .(в дополнительном коде) и удобное пред-
ставление очень больших чисел (с плавающей точкой), введение
еще одного типа данных и новых команд для численных расчетов
может вызвать недоумение у читателя. Однако это необходимо по
двум причинам. Во-первых, в некоторых применениях, особенно при
коммерческих расчетах (например, при составлении финансовых пла-
нов и выполнении бухгалтерских операций), операнды могут быть
очень большими числами, а результаты должны быть точными.
Представления в формах целых чисел и чисел с плавающей точкой
в таких случаях оказываются неприемлемыми. Суммы в десятки и
сотни миллионов долларов невозможно представить в длинном
слове. Мы предполагаем, что числа имеют два десятичных разряда
для центов. Десятичная точка задается программистом (команды,
реализующие операции над упакованными десятичными числами,
считают операнды целыми числами). Во многих ЭВМ наибольшим
целочисленным типом данных является 16-битовое слово, поэтому
в дополнительном коде можно представлять не все (десятичные)
пятиразрядные числа. Представление в формате с плавающей точкой
считается для таких применений неудовлетворительным, так как не
может обеспечить требуемую точность вычислений (см. разд. 13.2).
Еще одна причина использования упакованного десятичного
формата заключается в том, что преобразование символьного
' кода в дополнительный (или в представление с плавающей точкой)
при вводе и выводе происходит весьма медленно. Например,
при целочисленном вводе символьный код вначале преобразуется в
упакованное десятичное представление путем выделения младших
тетрад, а затем это представление преобразуется в дополнитель-
ный код по методу Горнера (см. разд. 7.4). Хотя арифметические
операции над числами в дополнительном коде выполняются быстрее,
чем над упакованными десятичными, при малом объеме вычислений,
но большом количестве операций ввода-вывода целесообразно сох-
ранить данные в упакованном десятичном формате. Очевидно,
такая ситуация, как и отмеченная ранее, характерна для коммер-
ззо
ческих применений. Поэтому во многих компиляторах Кобола для
внутреннего представления данных применяется упакованный деся-
тичный формат. Маловероятно, что упакованный десятичный формат
и соответствующие команды будут интенсивно использоваться прог-
раммистами, работающими на языке Ассемблера.
13.7. Команды,
реализующие операции
над упакованными
десятичными числами
Общие замечания. Операнды машинных команд в упакованном
десятичном формате должны находиться в памяти; они не могут
храниться в регистрах, и их нельзя определять как непосредственные
операнды. Упакованная десятичная цепочка определяется числом
содержащихся в ней цифр (а не числом байтов) и адресом ее
первого байта (с меньшим номером). Поскольку для каждой
цепочки требуется наличие в команде двух операндов, некоторые
арифметические команды имеют четыре или шесть операндов в
зависимости от того, замещает ли результат одну из цепочек или
загружается в другой получатель.
Хотя в командах с упакованными десятичными операндами
выдерживается большинство обычных соглашений об адресации,
приходится все-таки учитывать возможность возникновения особых
случаев и потенциальных проблем. Если число цифр в числе четное,
в старшем разряде будет лишняя нулевая цифра. Если же число
цифр равно нулю, цепочка занимает один байт, содержащий нулевую
цифру и знак « + ». (Конечно, можно представить и —0 десятичной
цепочкой, но CPU всегда формирует +0, когда результат операции
равен нулю и нет переполнения.)
Если результат операции меньше отведенной ему цепочки-по-
лучателя, его старшие разряды заполняются нулями. При перепол-
нении же возникает ошибка (особый случай), если установлен
бит разрешения прерывания десятичного переполнения (бит 7) в
PSW. Этот бит прерывания (как и бит прерывания целочисленного
переполнения) устанавливается или сбрасывается при входе в про-
цедуру в зависимости от того, определен или нет DV в маске
запоминания регистров в точке входа процедуры. Особые случаи
возникают также при зарезервированных операндах (запрещенная
тетрада в операнде или выход за пределы диапазона 0—31) и при
делении на нуль.
Так как операндами команд, реализующих операции над десятич-
ными числами, являются длинные цепочки байтов в памяти (как и
команд MOVC, СМРС, CVTSP и ряд других), они используют в
качестве рабочих регистры R0—R3, а имеющие отдельный операнд-
331
получатель,— также регистры R4 и R5. Регистры RO, R2 и R4 (если
последний задействован) после выполнения команды содержат нули;
регистры Rl, R3 и R5 (если он задействован) содержат адреса
байтов, в которых находятся старшие разряды первого, второго и
третьего (если он есть) десятичных операндов соответственно.
Эти адреса могут потребоваться программисту.
Заметим, что в отличие от большинства команд системы VAX
шестиоперандные команды, реализующие операции над десятич-
ными числами, могут действовать неправильно, если в памяти байты
получателя перекрываются с байтами любого другого операнда
(десятичной цепочкой).
Все команды, инициирующие операции над десятичными числа-
ми, обычным образом воздействуют на коды условий N, Z и V.
Они сбрасывают флаг С (за исключением команды MOVP, которая
этот флаг не изменяет).
Инициализация памяти. Для инициализации памяти на упако-
ванные десятичные константы применяются директивы .PACKED
со следующими форматами:
.PACKED десятичная_цепочка
.PACKED десятичная_цепочка, имя
Десятичная цепочка имеет знак и от 0 до 31 цифры. Если в ди-
рективу включено имя, ассемблер присваивает ему значение, равное
числу цифр в цепочке, поэтому имя можно использовать в команде
как литеральный операнд для определения числа цифр.
Пример 13.12. Определение десятичной цепочки. Когда ассемблер встречав г
директиву
DEC: .PACKED —2846,DGTS,
он ассемблирует константу как 02 84 6D в байтах, начиная с текущей ячейки. Значение
счетчика ячеек присваивается переменной DEC, а значение 4 — абсолютному
имени DGTS.
Поскольку в листингах ассемблерных программ содержимое памяти показывается
справа налево, байты с упакованным десятичным числом следуют <наоборот».
Если, скажем, строка с приведенной директивой была седьмой в программной секции
и до нее использовано 26 байт,.в листинге она будет иметь вид:
6D 84 02 001А 7 DEC: .PACKED —2846,DGTS
Команды. Команды, реализующие арифметические операции над
упакованными десятичными данными, имеют следующие форматы1:
|ADDP] упк\, чц2, упк2 А
ISUBPj 16чц1, упк\, чц2, упк2, чцплч, плч J
MULP чц\, упк\, чц2, упк2, чцпроизв, произв
DIVP чцдлт, длт, чцдлм, длм, чцчст, чет .
1 Здесь чц — число цифр, упк — адрес десятичного операнда, плч — получатель,
чцплч — число цифр получателя, произв — произведение, чцпроизв — число цифр
произведения, длт — делитель, чцдлт — число цифр делителя, длм — делимое,
чцдлм — число цифр делимого, чет — частное, чцчст — число цифр частного.— При-
меч, пер.
332
В этих и во всех других аналогичных командах число цифр
(чц) определяется в операнде-слове. По команде DIVP формиру-
ется целое частное и производится усечение к нулю, как и по цело-
численным командам DIV. Команде DIVP требуется в стеке рабо-
чая область из 16 байт; по ней выполняется декремент SP для ре-
зервирования этой области и восстанавливается для освобождения
области по завершении операции.
Пример 13.13. Сложение упакованных десятичных цепочек.
Предположим, что содержимое памяти следующее:
SALARIES #00 04 37 82 60 ОС
TRAVEL #64 09 00 ОС
После выполнения команды
ADDP6 #10, SALARIES, #7, TRAVEL, #12, EXPENDITURES
мы получаем результат, который начинается в EXPENDITURES:
EXPENDIIURES 00 00 05 01 91 60 ОС
Коды условий N, Z, V и С будут сброшены.
Формат команды MOVP:
MOVP чц, источник, получатель
Поскольку две сравниваемые цепочки могут иметь различное
число цифр, предусмотрены две команды сравнения СМРР:
СМРРЗ чц, упк\, упк2
СМРР4 чц1, упк1, чц2, упкШ.
В разд. 6.5 рассматривались команды, по которым осуществля-
ются взаимные преобразования упакованного десятичного представ-
ления, дополнительного кода и символьного кода. Эти команды
включены в таблицу, приведенную в следующем разделе.
Команды CLR, MOVA и TST для выполнения операций над
упакованными десятичными числами не предусмотрены.
13.8. Заключение
Система VAX имеет два стандартных формата чисел с плаваю-
щей точкой: одинарной точности (тип F) в длинном слове и двойной
точности (тип D) в тетраслове, а также два необязательных фор-
мата G и Н. Числа с плавающей точкой представляются в виде
±fX2₽, где и —127^р^127 (для форматов F и D).
Числа всегда представляются в нормализованной форме —
первый бит после точки равен единице и он не фигурирует во вну-
треннем представлении. Порядок числа хранится в смещенной фор-
ме, его поле содержит р + 2*—*, где t — число битов в поле порядка.
Форматы чисел показаны на рис. 13.1.
Так как мантисса числа имеет конечное число разрядов, резуль-
ззз
тэты выполнения арифметических операций не всегда оказываются
точными. Если не следить за Последовательностью вычислений,
могут возникнуть серьезные ошибки.
Десятичные числа хранятся в формате упакованной десятичной
цепочки — каждая десятичная цифра представлена тетрадой, а
знак находится в последней тетраде цепочки (см. рис. 13.9). Упако-
ванные десятичные числа и команды, реализующие над ними опе-
рации, используются в основном в компиляторах языков высокого
уровня, ориентированных на коммерческие применения, поскольку
обеспечивают высокую точность вычислений и позволяют опериро-
вать очень большими целыми (в системе VAX до 31 цифры).
Десятичные цепочки в командах определяются двумя операн-
дами: числом цифр в цепочке и адресом ее первого байта. Соответ-
ствующие команды используют регистры R0—R3, а шестиоперанд-
ные команды — еще и регистры R4 и R5.
Команды, реализующие операции над числами' с плавающей
точкой и упакованными десятичными числами, подчиняются обыч-
Таблица 13.2
Команды, реализующие операции над числами с плавающей точкой
Команда ‘ Тнп формата Операция
ADDx2 onl, on 2 ADDx3 onl, оп2, получатель SUBx2 onl, оп2 SUBx3 onl, on2, получатель MULx2 onl, on2 MULx3 onl, on2, получатель DIVx2 onl, on2 DIVx3 onl, on2, получатель CLRrf получатель MOVx источник, получатель MOVAx источник, получатель MNEGx источник, получатель TSTx on CMPx onl, on2 CVTxy источник, получатель СУГТЯхЬисточник, получатель АСВх предел, инкремент индекс, назначение Примечание. Все команды влияют x=F, D, G, Н » » » » » » » » » » х = В, W, L, F, G.H у— В, W, L, D, G, 11 х^у xy^DG или GD v = F, D, G, Н x=F, D, G, Н на коды условий о( onl + оп2-*оп2 onl + оп2-*получатель оп2—оп1-*оп2 оп2—onl -^получатель оп1*оп2-*оп2 onl *оп2-* получатель оп2/оп1-*оп2 оп2/ on 1-* получатель 0.0.-> получат ель источник-* получатель адрес источника-* полу- чатель —источник-*получат ель влияют только на коды условий источник (тип х), преоб- разованный в тип у—+по лучат ель- источник, преобразован- ный в L, округленный-»- -*получателъ индекс + инкремент-* -*индекс перейти к назначению, если инкремент>0 и ин- декс^предела или инкре- мент<0 и индекс^преде- ла бычным образом.
334
ным соглашениям об адресации операндов и воздействию на коды
условий. Они интерпретируют тип своих операндов в соответствии
с командой. Особые случаи в программе возникают при перепол-
нении, наличии зарезервированных операндов, делении на нуль и
исчезновении порядка в формате с плавающей точкой. Программист
может разрешать и запрещать прерывания, вызываемые перепол-
нением и исчезновением порядка.
Константы с плавающей точкой ассемблируются с помощью
директив .х_FLOATING(x = F, D, G или H). Некоторые неболь-
шие константы с плавающей точкой ассемблируются в литераль-
ном режиме. Литерал с плавающей точкой содержит трехбитовый
истинный порядок и три бита мантиссы (см. рис. 13.2). Десятичные
константы ассемблируются с помощью директивы .PACKED. Деся-
тичные операнды нельзя определять в регистровом, непосредствен-
ном и литеральном режимах.
Команды, рассмотренные в настоящей главе, приведены в
табл. 13.2 и 13.3.
Команды, реализующие операции *
над упакованными десятичными числами
Таблица 13.3
Команда Операция •
ADDP4 чц!, упк1, чц2, упк2 ADDP6 чц!, упк1, чц2, упк2, чцплч, плч SUBP4 чц!,упк1, чц2, упк2 SUBP6 чц!, упк!, чц2, упк2, чцплч, плч MULP чц!, упк!, чц2, упк2, чцпроизв, произв DIVP чц!, упк!, чц2, упк2, чцчст, чет MOVP чц, источник, получатель СМРРЗ чц, упк!, упк2 СМРР4 чц!, упк!, чц2 упк2 CVTLP длинное слово, чц, упк CVTPL чц, упк, длинное слово CVTPS чцупк, упк, чцек, ск CVTSP чцек, ск, чцупк, упк Примечание. Все- команды влияют на кс упк! + упк2-+упк2 упк! + упк2-+плч упк2—упк2-+упк2 упк2—упк!-+плч упк1♦упк2-+произв упк2/упк1-+чст источник-+получатель сравнивают упк! и упк2 влияют на коды условий длинное слово, в дополнительном ко- де, преобразованное в упакованное ЧИСЛО->1/ПК упк, преобразованное в дополнитель- ный кол,-^длинное слово упк, преобразованное в символьный код,-^ск ск, преобразованный в упакованное представление -+упк )ды условий обычным образом.
13.9. Упражнения
1. Какое число с плавающей точкой представлено каждым из следующих дан-
ных? Ответы представьте в десятичной системе, a) 0000BFC0, б)8000С40А,
в) 43000041, г) 430047АО, д) 00004080.
335
2. Найдите представление с плавающей точкой (типа F) следующих чисел:
а) 25, б) —2, в) 1048576 (=220), г) 5/8, д) 1, е) 21 3/16.
3. Напишите команды для вычисления 7х2+ 3,8ху—5у4, где
х и у — числа с плавающей точкой (типа F), хранящиеся, соответ-
ственно, в X и Y. Z)
4. Пусть содержимое регистра R5 составляется 8А004Е14, а регистра
R9—06004А31. Что будет в регистрах R5 и R9 и каково состояние кодов условий
Z и N после выполнения каждой из следующих команд: a) SUBF2 R9, R5,
6)SUBB2 R9, R5?
5. Найдите машинный код команды
MULD3# 1.5, Р5,— (Р8)
6. В некоторых ЭВМ любое число с плавающей точкой, содержащее нули во
всех битах мантиссы, интерпретируется как нуль с плавающей точкой. Почему
это не принято в системе VAX?
7. Выше было показано, что некоторые команды, реализующие операции над
числами с плавающей точкой, идентичны соответствующим командам для выполне-
ния операций над целыми числами. Так, команда CLRF эквивалентна команде
CLRL. Совпадает ли команда TSTF с командами TSTL или TSTW? Если команды
не одинаковы, поясните, в чем они различаются.
8. Как отличаются между собой операции, инициируемые командами MOVL,
MOVW и MOVF?
9. Напишите программный сегмент для вычисления площади под кривой
i/ = x2 посредством суммирования площадей прямоугольников (рис. ИЗ. 10). Счи-
тайте, что XI, Х2 и EPSILON содержат числа с плавающей точкой (типа F).
Для управления циклом воспользуйтесь командой АСВ.
10. Макрокоманды, определенные в примерах 11.5, 11.6 и 11.12, имеют аргумент
TYPE, задающий тип данных для их команд. Допустимы ли значения F, D, G и Н
для TYPE? Ответ поясните.
11. Покажите шестнадцатеричные формы наименьшего и наибольшего поло-
жительных чисел, которые можно представить числами с плавающей точкой (ти-
па D).
• 12. Приведите пример, показывающий, что ассоциативный закон сложения
не всегда справедлив в арифметике с плавающей точкой.
13. Мы доказали, что в арифметике с плавающей точкой системы VAX из тож-
дества a+b = b нельзя получить а = 0 на примере сложения 1/2 и 224 с получением
результата 224. Можно ли доказать то же самое, сложив 1/2 и 223? Выполните такое
сложение, чтобы обосновать свой ответ.
14. Требуется умножить два положительных числа с плавающей точкой. На
какое максимальное число разрядов придется сдвигать биты произведения для
нормализации результата? Почему?
15. Назовите этапы преобразования числа в формате F с плавающей точкой
в число, представленное в формате D, без использования команды CVTFD. 16 17 18
16. Напишите процедуру CVTFD для решения задачи из упр. 15. Процедура
должна устанавливать коды условий в PSW вызывающей программы (в кадре
вызова) так же, как это делается по команде CVTFD.
17. Перечислите этапы преобразования числа в формате с плавающей точкой
в число, представленное в формате типа F, без использования команды CVTDF
(результат должен быть округленным).
18. Приведите шестнадцатеричное представление числа с плавающей точкой
336
Рис. 13.10. Вычисление площади
под кривой у—х1
в формате двойной точности, которое вызовет переполнение, будучи первым опе-
рандом команды CVTDF.
19. Напишите процедуру CVTDF для решения задачи из упр. 17. Процедура
должна устанавливать коды условий в PSW вызывающей программы (в кадре
вызова) так же, как это делается по команде CVTDF.
20. Назовите этапы преобразования целого числа в формате длинного слова
в число с плавающей точкой (типа F), не пользуясь командой CVTLF.
21. Напишите процедуру CVTLF для решения задачи из упр. 20. Процедура
должна устанавливать коды условий в вызывающей программе (в кадре вызова)
так же, как это делается по команде CVTLF.
22. Модифицируйте процедуру CVTFL на рис. 13.8 так, чтобы она возвращала
округленный результат, а не усеченный. Процедура должна выполняться так, как
если бы использовалась команда CVTRFL, а не команда CVTFL.
23. Перечислите этапы преобразования числа с плавающей точкой в символьную
цепочку следующего формата:
.Ь1 ЬъЬз... 622^23^24 X 2Ср,
где bь...,b24 — биты мантиссы, а р — порядок. Напишите процедуру, по которой
осуществляется преобразование. (Конечно, существуют более естественные и ком-
пактные форматы вывода результатов, но их формирование потребует выполнения
большего объема работы. Попробуйте выбрать другие форматы для вывода и напи-
шите соответствующие процедуры.)
24. Представьте числа 112485 в следующих форматах: длинном слове, F, G, сим-
вольном коде и упакованном десятичном.
25. Почему упакованные десятичные операнды нельзя определять как непо-
средственные? (Причина связана со способом интерпретации спецификатора опе-
ранда во время выполнения.)
। 26. Напишите определение макрокоманды TSTP, которая действует как коман-
да проверки упакованных десятичных данных. Она должна иметь два аргумента
NUM____DGTS и PKD и модифицировать коды условий N и Z, показывая, что ука-
занная десятичная цепочка является отрицательной или нулевой. (Если по ва-
шей макрокоманде явно, проверяются знак и цифры десятичной цепочки, она ока-
жется довольно сложной. Есть очень короткое решение.)
27. Напишите полную программу для считывания двух чисел с терминала,
преобразования их формата в упакованный десятичный, умножения и печати ре-
зультата.
337
Глава 14
Символьные цепочки
14.1. Общие замечания
В ЭВМ семейства VAX предусмотрена мощная система команд
для выполнения операций над символьными цепочками. Эти команды
весьма удобны при обработке входных данных и подготовке данных
для вывода. Некоторые команды ориентированы на редактирование
текста и выполнение иных операций, в которых обрабатываемые
данные сформированы как длинные текстовые блоки. Кроме уже
рассмотренных команд MOVC3 и СМРСЗ, в VAX имеются более
сложные команды пересылок и сравнений, а также команды поиска
цепочки из заданных символов (подцепочки), преобразования
символов и редактирования числовых данных для вывода. Все эти
операции можно запрограммировать и с помощью других команд,
но наличие одной команды, позволяющей решить сложную задачу,
упрощает работу программиста, уменьшает вероятность возникно-
вения ошибок и ускоряет выполнение программ.
Символьная цепочка представляет собой последовательность
символов, например:
/CHAPTER 14/
Эта символьная цепочка заключена в ограничители — две
наклонных черты. Однако в контексте операций над символьными
цепочками мы определяем символьную цепочку как последователь-
ность символьных .кодов (в системе VAX применяется код ASCII),
занимающих смежные байты памяти. (Точнее, такую последова-
тельность нужно было бы назвать представлением символьной це-
почки, но для простоты мы не будем этого делать.) Пример символь-
ной цепочки:
STRING
43 48 41 50 54 45 52 20 31 34
С учетом функций команд, реализующих операции над символь-
ными цепочками, подобное определение оказывается несколько ог-
338
раниченным. Команды выполняются при наличии любой цепочки
байтов в памяти; байты могут содержать любые восьмибитовые
наборы, даже не входящие в код ASCII. В наиболее часто встреча-
ющихся ситуациях в байтах хранятся символьные коды.
Символьная цепочка определяется в команде длиной (или числом
байтов) и адресом ее первого байта. (Цепочка не может находиться
в регистре или определяться как непосредственный операнд.) Во
всех командах длина задается операндом-словом.
Как и в других представлениях, например упакованном десятич-
ном и символьном коде, первый, или самый левый, байт находится
в памяти по наименьшему адресу, а остальные символы следуют
в естественном порядке, поэтому на диаграммах размещения сим-
вольных цепочек в памяти байт с наименьшим адресом обычно
показывается слева.
Поскольку выполнение операции над символьными цепочками
связано с обработкой множества байтов операндов переменной
длины, CPU привлекает несколько регистров в качестве рабочих
(для указателей и счетчиков), как это сделал бы программист,
составляющий циклы для выполнения операций. Во многих случаях
данные в регистрах после выполнения команды оказываются весьма
полезными для программиста. Так, в команде LOCC просмотра
символьной цепочки на предмет выявления заданного символа CPU
использует регистр R1 как указатель текущего анализируемого бай-
та. По завершении команды в R1 оказывается адрес байта, содер-
жащего отыскиваемый символ (если, конечно, он в цепочке есть).
Конкретные задействуемые регистры и остающиеся в них данные
определяются для каждой команды.
14.2. Команды MOVC и СМРС
Ранее мы уже встречались с командами MOVC3 и СМРСЗ:
MOVC3 длина, источник, получатель
СМРСЗ длина, цепочка 1, цепочка2
По команде MOVC3 цепочка, начинающаяся в источнике, копи-
руется в байты, начинающиеся в получателе, а по команде СМРСЗ
сравниваются цепочки, начинающиеся в цепочке! и цепочке2. В обе
команды включено по одному операнду длины, так как предполага-
ется, что цепочки имеют одинаковую длину. Однако существуют
варианты этих команд, допускающие цепочки разной длины:
MOVC5 длист, ист, заполн, длплч, плч
СМРС5 дл1, цеп1, заполн, дл2, цеп2
Третьим операндом служит символ-заполнитель (filler), который
применяется при необходимости «удлинения» более короткой цепоч-
339
ки. Все операнды длины представлены словами, как и в командах
MOVC3 и СМРСЗ.
Команда MOVC5 копирования символьной цепочки. При выпол-
нении команды MOVC5, когда длина источника меньше длины полу-
чателя, в байты, остающиеся в конце цепочки-получателя, поме-
щаются символы-заполнители. Если же длина источника больше
длины получателя, лишние символы в конце цепочки-источника не
копируются. Копирование цепочки иллюстрируется рис. 14.1. Ко-
нечно, при одинаковых длинах цепочек источник копируется в по-
лучатель, как и в случае выполнения команды MOVC3.
Пример 14.1. Копирование симЬольных цепочек. Пусть BUFFER — область
памяти, где формируется строка для печати 1^ак часть таблицы, содержащей не-
сколько столбцов шириной в 20 позиций (пробелов) каждый. В регистре R8 хранит-
ся адрес символьной цепочки, которая должна размещаться в текущей строке
третьего столбца таблицы, а в регистре R9 — значение ее длины. Элемент от пре-
дыдущей строки может быть длиннее текущего, поэтому при обычной передаче
новой строки в буфер напечатанная строка может содержать лишние символы.
По приведенной ниже команде текущая цепочка пересылается в буфер, и лишние
позиции в столбце заполняются пробелами:
MOVC5 R9,(R8), #АА|. |, #20,BUFFER4-40
Пример 14.2. Заполнение строки одинаковыми символами. Если длина источ-
ника в команде MOVC5 равна нулю, вся цепочка-получатель заполняется симво-
лами-заполнителями. По следующей команде в памяти создается строка из звездо-
чек, начиная с LINE:
MOVC5 #0,0, #АА|*|, #80, LINE
Так как длина цепоЧки-источника равна нулю и к ней CPU не обращается, ее
адрес может быть произвольным; для простоты в команде MOVC5 он указан нулевым.
По команде MOVC5 устанавливаются коды условий, показы-
вающие отношение между длиной источника и длиной получателя.
Например, за командой MOVC5 может следовать команда
BGTR TRUNC
для перехода к некоторому специальному действию, если в получате-
ле оказалась усеченная копия цепочки-источника. При выполнении
команд MOVC5, как и при выполнении MOVC3, используются регистры
а) Если длист<длплч
Источник (ист) Получатель (плч)
Рис. 14.1. Действие команды MOVC5
340
RO—R5 и в них остаются следующие данные: в R0 — число байтов
цепочки-источника, не скопированных в получатель; в R1 — адрес
байта, находящегося за последним скопированным байтом;
в R2 — 0; в R3 — адрес байта, находящегося после цепочки-полу-
чателя; в R4 и R5 — 0.
Команда MOVC5 выполняется правильно, даже если источник
и получатель перекрываются.
Пример 14.3. Предположим, как и в примере 14.1, что требуется пересылать
символьные цепочки в буфер, содержимое которого печатается как часть таблицы.
Если цепочка слишком велика для своего столбца, остаток ее необходимо напеча-
тать в следующей строке. При отсутствии в строке таких длинных элементов после
нее оставляется чистая строка (из пробелов). Предполагается, что элемент не занит
мает более 40 позиций и что для дополнительной строки предусмотрен буфер BUF2.
M0VC5 *О,О,*ЛА/ /,*80,BUF | Пробелы BUF2
COL.li
C0L.2I
C0L.3I M0VC5 R9,CRB),*ЛА/ /,*20,BUFFER+40 • ? 1 Скопировать цепочку а
1 столбец
BLEQ COL.4» 1 Перейти, если убирается
COL.4I MOVCS RO,(R1),*ЛА/ /,*20,BUF2+40 1 Скопировать лишние байты
PRINTCHR8 BUFFER,*80
I Печать строки таблицы
PRINTCHR8 BUF2,*80
I Печать дополнительной
I строки
Отметим, что, если опустить команду BLEQ, показанный фрагмент программы
будет выполняться правильно. При длине копируемого по первой команде MOVC5
элемента, не превышающей 20 байт, регистр R0 содержит нуль, поэтому второй
командой MOVC5 20 пробелов пересылаются в BUF2 + 40.
Команда СМРС5 сравнения символьных цепочек. Если длины
сравниваемых по команде СМРС5 цепочек различны, CPU воспри-
нимает более короткую цепочку как «растянутую» с помощью симво-
ла-заполнителя до длины большей цепочки. (Никаких изменений
цепочек в памяти, конечно, не производится.) Сравнение выполня-
ется (как и по команде СМРСЗ) последовательно, байт за байтом,
341
и прекращается, когда оказывается, что сравниваемые байты не рав-
ны или достигнут конец цепочек. Коды условий показывают отно-
шение между последними сравниваемыми байтами: N= 1, если байт
цепочки! меньше байта цепочки2 (аналогично целым числам в допол-
нительном коде), Z = l, если байты равны (т. е. достигнут конец
цепочек и различающихся байтов не было), и С = 1, если байт цепоч-
ки! меньше байта цепочки2 (как беззнаковые целые числа). Флаг
V сбрасывается.
При выполнении команд СМРСЗ и СМРС5 используются реги-
стры R0—R3. По завершении действия любой из команд в этих
регистрах остаются следующие данные:
в R0 — число байтов, оставшихся в цепочке! (с учетом байта, на
котором закончилось сравнение, если цепочки не равны, но без
учета байтов с заполнителями, если они есть);
в R1 —адрес байта в цепочке!, где сравнение прекращено, т. е.
адрес первого байта в цепочке!, который не равен соответствующе-
му байту цепочки2, если такой байт есть; в противном случае —
адрес байта за концом цепочки!;
в R2 — то же, что и в R0, но для цепочки2,
в R3 — то же, что и в R1, но для цепочки2.
Пример 14.4. Команда СМРС5. Предположим, что содержимое регистра R5
составляет хххх0012, а регистра R7 — хххх0007 (где х обозначает неиспользуемые
данные). Пусть, далее, в памяти хранятся две цепочки:
STRING1 STRING2
Cardiff by the Sea Cardiff
При выполнении команды
СМРС5 R5.STRING1, #АА| |,R7, STRING2
цепочка STRING2 дополняется 11 пробелами. Сравнение закончится на девятом
байте: код 62ie буквы «6» в STRING1 сравнивается с кодом 2016 второго добавлен-
ного пробела в STRING2. Все четыре кода условия будут сброшены, так как
62>20, а в регистрах в RO — R3 окажутся следующие данные:
в R0 — ОООООООА (число байтов, оставшихся в STRING1);
в Rl — STRING 1+8 (адрес буквы </»);
в R2 — 00000000 (в STRING2 байтов не осталось);
в R3— STRING2 + 7 (а не STRING2+8).
14.3. Команды поиска символов
В системе VAX имеются пять команд поиска символов. К ним
относятся команды, по которым отыскиваются либо пропускаются
указанный символ или любые символы из определенного множе-
ства, а также команда поиска заданной подцепочки.
Команды LOCC и SKPC поиска или пропуска заданного символа.
По команде LOCC (найти символ) осуществляется сканирование
символьной цепочки до первого появления заданного символа, а по
команде SKPC (пропустить символ) — до первого появления симво-
ла, отличающегося от заданного. Форматы команд:
LOCC символ, длина, цепочка
SKPC символ, длина, цепочка
342
«Результатом» выполнения этих операций является адрес байта,
на котором поиск заканчивается, однако операнд-получатель в
командах отсутствует. При просмотре цепочки в качестве счетчика
и указателя используются регистры R0 и R1. Результат представлен
следующими значениями:
в R0 — число оставшихся в цепочке байтов, включая и найденный
символ (если он найден);
в R1 — адрес найденного байта или, если байт не найден, адрес
байта, находящегося за концом цепочки.
Код условия Z сброшен, если байт заданного вида найден, и уста-
новлен — в противном случае. Все остальные коды условий сбро-
шены.
Таким образом, команды, следующие за LOCC и SKPC, могут
задействовать R1 в качестве указателя найденного байта (после
проверки Z на то, что он найден). Регистры R0 и R1 можно исполь-
зовать для повторного поиска в тех случаях, когда требуется выявить
все появления отыскиваемого символа или просмотреть сегмент,
разделенный двумя такими символами. Рассмотрим пример поиска
символа, где реализуется очень простая обработка сегментов —
их подсчет.
Пример 14.5. Поиск символа. Предположим, что в памяти по адресу TEXT
находится цепочка, содержащая несколько подцепочек, разделенных наклонной
чертой. Пусть LEN — слово, содержащее длину цепочки. Такой цепочкой может
быть, скажем, следующая:
/October 3/7:30 P.M. /AGENGA/
Приведенные ниже команды подсчитывают число подцепочек.
$ ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ! RO длина оставшейся цепочки
1 Rl адрес оставшейся цепочки
1 R5 счетчик
1
CLRL RS 1 Сбросить счетчик
MOVAB TEXT+1,R1 1 Байт после первой /
MOVW LEN,RO 1 Длина цепочки
1
MSGSi DECL RO 1 Не учитывать первую /
LOCC ,RO,1(Rl) 1 Сканировать до следующей /
BEQL DONE 1 Перейти, если нет /
INCL R5 1 Учесть подцепочку
BRB MSGS
DONEi
Необходимо тщательно следить за правильными определениями длин и адресов
в начале каждого поиска; нужно адресовать байт, находящийся после найденной
наклонной черты, чтобы она не была «найдена» еще раз.
Если обработка каждого символа или сегмента, найденного
343
с помощью команды LOCC, включает печать, копирование цепочек
или еще какие-то операции, при выполнении которых содержимое
регистров R0 и R1 модифицируется, его необходимо сохранять в
других" регистрах, чтобы адрес и длина оставшегося сегмента источ-
ника не были потеряны. (Эта проблема характерна для всех команд,
реализующих операции над символами.)
Команда SKPC особенно удобна для пропуска пробелов и поиска
в символьной цепочке «непробельных» данных. Допускается совме-
стное применение команд LOCC и SKPC для обработки числовых
входных данных в свободном формате.
Пример 14.6. Обработка входных данных в свободном формате. Процедура
CVTCL, приведенная на рис. 14.2, отыскивает в символьной цепочке первую под-
цепочку — символьный код числа — и преобразует ее в дополнительный код. Пред-
полагается, что символьная цепочка не содержит ни одного или содержит несколь-
ко символьных кодов чисел, разделенных минимум одним пробелом. По команде
SKPC пропускаются пробелы и находится начало первого символьного кода, а по
команде LOCC находится пробел в конце символьного кода. Процедура CVTCL
возвращает адрес байта, следующего за символьным кодом числа, и длину остав-
шейся части символьной цепочки, что позволяет вызывать эту процедуру много-
кратно для обработки в цепочке каждого числа. ,
.PSECT CVTCL
ПРОЦЕДУРА CVTCL (STRING, LENGTH, NUMBER, NEXT, NEWLEN)
Эта процедура отыскивает в символьной цепочке первое появление числа
в символьном кодв, преобразует его в длинное слово ( в дополнительном
коде) и возвращает адрес следующего байта цепочки и оставшуюся длину.
Предполагается, что числа в символьной цепочке разделены пробелами.
ВХОДНЫЕ- АРГУМЕНТЫ
STRING символьная цепочка
LENGTH число байт в цепочке (слово)
ВЫХОДНЫЕ АРГУМЕНТЫ
NUMBER представление в дополнительном коде первого
числа, найденного в цепочке (длинное слово)
NEXT адрес байта цепочки, находящегося после
найденного и прообразованного числа. (Слисок
аргументов содержит адрес длинного слова,
о котором >тот адрес должен быть запомнен).
NEWLEN число оставшимся в цепочке байт (слово)
RO возвращает флаг о том, было ли найдено число в цепочке!
1 успех, О неудача.
В случае неудачи никакие значения в трех выходных аргументах
не запоминаются.
Контроль запрещенных символов и количества цифр отсутствует.
МЕТОД
344
I Число в символьном коде начинается с байта, содержащего знак (им
I может быть пробел), после которого следуют цифры (все в символьном
I коде). Команда преобразования требует адреса цепочки (то есть адрес
I знака) и числа цифр.
I Команда SKPC пропускает все старшие пробелы. Она останавливается
I на первом символе* отличающемся от пробела, которым будет знак или
I первая цифра (если знак представлен пробелом). В последнем случае
I указатель уменьшается на 1. Затем команда LOCC отыскивает пробел,
I находящийся за числом в символьном коде. Когда найден пробел или
I достигнут конец всей символьной цепочки, R1 срдержит адрес байта,
I находящегося за числом в символьном коде.
?
PKBi .BLKB 16
I
I СМЕЩЕНИЯ ДЛЯ СПИСКА АРГУМЕНТОВ
;
STRINB - 4
LENGTH - в
NUMBER - 12
NEXT - 16
NEWLEN - 20
I ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ! R2-R3 используют команды CVTSP и CVTPL
I R5 длина цепочки
I R6 адрес числа в символьном коде
I R7 количество цифр в числе
.ENTRY CVTCL,*M<R2,R3,R5,R6,R7>
I Найти начало числа в символьном коде
MOVW BLENGTH(AP),RS j Длина цепочки
BLEQ NONE | Нет числа, если длина < 0
SKPC (ГА/ /,RS,tSTRING(AP) । Искать не-пробел
BEQL NONE । Нот числа, если все пробелы
MOVL R1,R6 । Адрес первого не-пробела
СМРВ (R6),#ЛХЗО । Проверить на цифру
BLSS LSN । Перейти если не-цифра
DECL R6 । Вернуться на знак (пробел)
I
I Найти конец числа в символьном коде
I
LSNi LOCC (ИА/ /,RO,(RO) | Сканировать до пробела
MOVL R1,QNEXT(AP) । Запомнить адрес следующего байта
MOVL RO,tNEWLEN(AP) | Запомнить оставшуюся длину
SUBL3 R6,R1,R7 | Количество байт в числе
DECL R7 । Количество Цифр
I
I Преобразовать
I
CVTSP R7,(R6),R7,PKD
CVTPL R7,PKD,QNUMBER(AP) | Запомнить дополнительный код
I
I Установить флаг и осуществить возврат
345
MOVL #1,RO | Успех
RET I Возврат
I
NONEi CLRL RO I Неудача
RET I Возврат
.END
Рис. 14.2. Процедура CVTCL
Команды SCANC и SPANC поиска множества символов. По
команде SCANC (сканировать символы) в цепочке отыскивается
любой из множества символов, а по команде SPANC (пропускать
символы) — любой символ, не входящий в указанное множество.
(Очевидно, команду SPANC можно считать избыточной, так как
можно определить дополнительное множество символов и приме-
нить команду SCANC. Однако наличие команды SPANC упрощает
для программиста решение ряда задач.) В некоторых ситуациях
требуется найти символ из специальной группы символов. Напри-
мер, компилятор отыскивает в программном операторе знак какой-
либо операции или ограничитель. Может понадобиться подсчитать
гласные в длинном тексте. При обработке входных численных дан-
ных с помощью команды SPANC можно отыскать «плохие» символы
(не являющиеся цифрами, пробелами и знаками « + » или «—»).
Форматы этих команд:
SCANC длина, цепочка, таблица, маска
SPANC длина, цепочка, таблица, маска
Таблица и маска применяются для задания отыскиваемых сим-
волов. Длина таблицы обычно составляет 256 байт. Содержимое
каждого байта цепочки, интерпретируемое как беззнаковое'целое
число, находится в диапазоне 0...255. Оно используется в качестве
индекса таблицы; если байт в цепочке содержит число п, то анали-
зируется байт по адресу таблицы + п (входит ли байт цепочки в
требуемое множество). Самый простой способ отделить нежелатель-
ные символы от отыскиваемых — поместить в соответствующие
места таблицы нулевые и ненулевые элементы. Во многих больших
ЭВМ предусмотрена аналогичная SCANC команда, по которой осу-
ществляется сканирование цепочки до достижения байта, имеющего
ненулевой элемент таблицы. Команды системы VAX являются более
гибкими. Над байтом таблицы и операндом-маской команды выпол-
няется логическая операция AND. Если результат оказывается нену-
левым для SCANC или нулевым для SPANC, команда «останавлива:
ется» на текущем байте (рис. 14.3). Применение маски означает, что
можно построить одну таблицу для нескольких команд SCANC и
SPANC, чтобы вести поиск различных множеств символов в разное
время.
Пример 14.7. Таблица для команд SCANC и SPANC. При лексическом анализе
компилятор отыскивает и выделяет отдельные элементы (называемые лексемами),
346
образующие оператор. К лексемам можно отнести имена переменных, знаки ариф-
метических операций, константы и т. д. Ниже приведены некоторые виды элементов,
которые необходимо включить в таблицу, используемую для выделения лексем.
Символьная группа
Арифметический оператор
Оператор отношения
Терминатор (пробел или ;)
1 Буквы (кроме Е)
Е
Цифры
Конечно, эта таблица является неполной, в ней не учтены многие специальные
случаи, которые должен учитывать компилятор. В этом примере мы^сотим показать,
каким образом можно выбирать элементы таблицы и маску, чтобы гибко определять
различные множества символов.
Так, если требуется отыскать первый арифметический оператор, следует восполь-
зоваться командой SCANC с маской АХ01. Можно найти первый символ имени
---------_ч --------- - - ---------Ал,,л "---------- -------------- имени,
Элемент таблицы (двоичный)
00000001
00000010
00000100
00010000
00110000
01100000
переменной, отыскивая букву с маской ХЮ. Возможно найти и конец
применив команду SPANC с маской *Х70 для пропуска букв и цифр.
Почему в элементе для цифр установлены два бита, а для символа Е
особый элемент? Символ Е может появиться как буква, поэтому установлен
но этот же символ может появиться в константе с плавающей точкой для указания
ее порядка. Следовательно, иногда желательно включить Е в цифры, пользуясь
маской ЛХ20. Маска АХ40 отделяет цифры от буквы Е.
Таблицу можно построить во время ассемблирования (см. ниже). Для просто-
ты все байты в таблице, не упомянутые выше, заполняются нулями. Проверьте по
коду ASCII в приложении С, правильно ли размещены элементы:
введен
бит 4,
TABLE: -BYTE
.BYTE
.BYTE
. BYTE
.BYTE
.BYTE
.BYTE
OC323
4.OC93
l,l,O,l,O,l
ЛХ6ОС1ОЗ
О,4,2,2,2,О,О
ЛХ1ОС43,ЛХЗО,ЛХ1ОС213
ОС63
$ Специальны» символы ASCII
; Пробел и специальны» символы
I Цифры
j I !<->?•
I Буквы
I Вспомогательны» символы
347
.BYTE AX10C263 | Строчные буквы
«BYTE ОС 1333 | Другие символы
Отметим, что если бы мы были уверены в появлении в цепочке только допусти-
мых кодов ASCII, то достаточно было бк построить таблицу из 128 байт, так как
обращения к ней в диапазоне от TABLE+128 до TABLE+.255 не предвидится.
Когда по команде SCANC или SPANC находится указанный
байт или достигается конец цепочки, в регистрах R0—R3 возвра-
щаются данные:
в R0 — число байтов, оставшихся в цепочке, включая и байт, на ко-
тором закончен поиск, если найден элемент таблицы;
в R1 —адрес байта в цепочке, по табличному элементу которого
закончен поиск, если он есть; если же его нет — то адрес байта,
следующего за таблицей;
в R2 — 0;
в R3—адрес таблицы.
Код условия Z сбрасывается, если поиск завершается нахожде-
нием табличного элемента, и устанавливается в противном случае.
Остальные коды условий сбрасываются.
Регистры R0 и R1 можно использовать так же, как и после выпол-
нения команд LOCC и SKPC — для обработки найденного символа,
предшествующего ему сегмента цепочки или ее оставшейся части.
Команда МАТСНС для обнаружения соответствия символьной
цепочки шаблону. По команде МАТСНС в символьной цепочке отыс-
кивается заданная подцепочка, а не единичный символ. Операция,
выполняемая по этой команде, называется обнаружением соответ-
ствия шаблону, а отыскиваемая подцепочка — шаблоном или объект-
ной цепочкой. Мощная команда МАТСНС весьма удобна при редак-
тировании текста. Ее формат1:
МАТСНС длшабл, шаблон, длцеп, цепочка
После выполнения команды МАТСНС в регистрах R0—R3 оста-
ются следующие данные:
в R0 — 0, если соответствие обнаружено, в противном случае —
длина шаблона;
в R1 — адрес байта, находящегося за шаблоном, если соответствие
обнаружено, в противном случае — адрес шаблона;
в R2 — число байтов, оставшихся в цепочке после первого обнару-
жения шаблона (если он найден), в противном случае—нуль;
в R3 — адрес байта, следующего за первым обнаруженным шабло-
ном в цепочке (если он найден), в противном случае— адрес байта,
находящегося за цепочкой.
Код условия Z установлен, если соответствие обнаружено, и сбро-
шен, если оно не найдено. Остальные коды условий сброшены.
Наиболее полезная информация содержится в регистрах R2 и
1 Здесь длшабл — длина шаблона, длцеп — длина цепочки.— Примеч. пер.
348
R3 (а также указывается состоянием флага Z). Вычитание длины
шаблона из адреса в регистре R3 дает адрес байта, с которого шаблон
начинается в цепочке. Отметим, что после выполнения четырех
рассмотренных выше команд флаг Z сбрасывается, если поиск за-
вершается успешно, в то время как после выполнения команды
МАТСНС в случае успешного поиска флаг Z устанавливается.
Пример 14.8. Команда МАТСНС. Предположим, что содержимое регистра R6
составляет хххх0023, регистра R7 — ххххОООб, и в памяти находятся следующие
цепочки:
LINE
This is the house which Jack built.
PATRN
which ,
По команде
МАТСНС R7,PATRN,R6, LINE
в регистрах RO—R3 формируются следующие данные:
в R0—00000000 (соответствие обнаружено);
в R1 —PATRN + 5 (адрес байта после шаблона);
в R2 — 0000000С (число байтов в цепочке после обнаружения соответствия);
в R3 — LINE + 23 (адрес пробела после слова <which>).
Флаг Z установлен в состояние 1 (соответствие обнаружено).
Пример 14.9. Удаление подцепочки.
Процедура DELETE, приведенная на рис. 14.4, находит в цепочке заданную
подцепочку и удаляет ее, сдвигая все символы, оставшиеся после обнаружения
соответствия. Отметим, что данные, сформированные в регистрах R2 и R3 при вы-
полнении команды МАТСНС, служат операндами в команде MOVC3. Кроме того,
хотя длины операндов и являются словами, длина шаблона преобразуется в длин-
ное слово, так как она используется при вычислении адреса (копии шаблона в
цепочке).
.PSECT DELETE
ПРОЦЕДУРА DELETE (STRING, STR.LEN, PATTERN, PAT.LEN)
Эта процедура находит первое появление шаблона о символьной
цепочке и удаляет его. Код условия Z в запомненном PSW устанав-
ливается, если шаблон найден в цепочке и удален.
ВХОДНЫЕ АРГУМЕНТЫ
STRINB символьная цепочка
STR.LEN длина цепочки (слово)
PATTERN символьная цепочка, удаляемая из STRINB
PAT.LEN длина PATTERN (слово)
ВЫХОДНЫЕ АРГУМЕНТЫ
/
STR.LEN изменяется на новуш длину цепочки
после удаления шаблона
) СМЕШЕНИЯ ДЛЯ СПИСКА АРГУМЕНТОВ
I
STRINB - 4
STR.LEN - в
PATTERN - 12
PAT.LEN - 16
I
349
I ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ! R2 длина сегмента цепочки,
I находящегося за шаблоном
I R3 адрес байта в цепочке после
I шаблона
I R4-RS использует команда N0VC3
I R6 длина цепочки (слово)
I R7 длина шаблона (длинное с;лево,
I используется в адресных вычислениях)
I Rfl адрес, где в цепочке начинается
I шаблон
I
.ENTRY DELETE,*M<R2,R3,R4,RS,R6,R7,R8>
I
MOVW «STR.LEN(AP),R6 ; Длина цепочки
CVTWL «PAT.LEN(AP),R7 | Длина шаблона
. MATCHC R7,«PATTERN(AP),R6,«STRING(AP) | Искать шаблон
BNEQ RETURN | Возврат, если шаблона нет
SUBL3 R7,R3,R8 > Начало шаблона в цепочке
MOVC3 R2,(R3),(R8) | Передать оставшийся сегмент
SUBW3 R7,R6,«STR.LEN(AP) | Новая длина цепочки
BISB2 t*X04,4(FP) । флажок успеха Z
RETURN! RET t
.END
1 Рис. 14.4. Процедура DELETE
В гл. 7 рассматривалась проблема отыскания в таблице кон-
кретного элемента и обсуждались два Алгоритма поиска — после-
довательного и двоичного (последний требует сортировки таблицы).
Действие команды МАТСНС напоминает последовательный поиск.
Ее (как и другие команды, реализующие операции над символами)
можно применять, даже если байты в операндах не содержат коды
ASCII. Ниже приводится пример применения команды МАТСНС
для поиска целочисленного данного, где показано, что пользовать-
ся ею следует осторожно. '
Пример 14.10. Применение команды МАТСНС для выполнения операций над
несимвольными данными. Предположим, что начинающаяся в TABLE таблица
содержит 100 элементов по 20 байт в каждом. Любой элемент начинается с ключа,
которым является целочисленное слово. Необходимо найти адрес элемента, ключ
которого соответствует ключу в слове KEY. Для этой цели можно воспользоваться
командами:
МАТСНС #2,KEY,*2000,TABLE $ Искать ключ
BNEQ NOT_FOUND ; Перейти, если нет соответствия
8UBL2 #2,R3 ; Адрес элемента
Но всегда ли применение этих команд дает правильный результат? Проблема
заключается в том, что байты ключа могут соответствовать двум байтам в других
полях. Допустим, что в каждый элемент таблицы входят ключ, целое число в формате
длинного слова и 14-байтовая символьная цепочка и что мы отыскиваем элемент»
ключ которого равен 207416. В представленном далее сегменте таблицы показан?,
в каких местах можно обнаружить 2074, но все результаты, кроме последнего,
оказываются неправильными. (Так как таблица состоит из целочисленных данных»
байт с наименьшим адресом каждого элемента таблицы показан справа. Отыски-
ваемый ключ, или шаблон, содержит 74 в первом байте и 20—во втором.)
350
Символы
хххххххххххххххххххххххххххх
74ХХХХХХХХХХХХХХХХХХХХХХХХХХ
ХХХХХХХХХХХХХХХХХХХХХХХХХХ20
хххххххх2074хххххххххххххххх
хххххххххххххххххххххххххххх
хххххххххххххххххххххххххххх
Длинное слово Ключ
хххххх20 74хх *- первый
хх2074хх хххх байт
74хххххх хх20
хххххххх хххх
хххххххх 2074
хххххххх 2074
Для успешного завершения поиска элемента с ключом 2074 нужно проверить
адрес в регистре R3 после выполнения последней из приведенных выше команд
(кратно ли его расстояние от TABLE размеру элемента). Команда МАТСНС и про-
верка должны выполняться в цикле, чтобы следующее сканирование можно было
начать с байта, находящегося после каждого неправильно найденного ключа. Чита-
телю рекомендуется самостоятельно дописать программу.
14.4. Преобразование
символьных цепочек
Команда MOVTC преобразования символьной цепочки. Команда
MOVTC (переслать преобразованные символы) преобразует цепочку
байтов на основе таблицы преобразования, определенной как один
из операндов команды. Ее можно использовать для преобразова-
ния какого-либо символьного кода (например, EBCDIC) в код
ASCII (или наоборот), для замены всех строчных букв на прописные,
изменения некоторых управляющих символов, выделения и объеди-
нения подполей в сегменте данных и «перепутывания» букв в сооб-
щениях с целью образования криптограмм.
Формат команды1:
MOVTC длист, источник, заполн, таблица, длплч, получатель
Таблица преобразования обычно имеет длину в 256 байтов, а
обращение к ней производится так же, как я при выполнении команд
SCANC и SPANC. Каждый байт цепочки содержит беззнаковое
целое двоичное число (в диапазоне 0...255), являющееся индексом
таблицы, из которой извлекается значение, подставляемое вместо
этого байта. Другими словами, содержащееся в байте число п пре-
образуется в число из байта, адрес которого равен адресу таблицы
плюс п (рис. 14.5).
Как и в команде MOVC5, если длина источника меньше длины
получателя, последний дополняется символами-заполнителями.
Если же длина источника превышает длину получателя, лишние
байты источника просто не преобразуются. Источник и, получатель
могут перекрываться (в случае очень длинной цепочки-источника
они могут занимать одну и ту же область), но получатель не должен
перекрываться с таблицей преобразования. Все коды условия пока-
зывают отношение между длинами источника и получателя. По за-
1 Здесь длист — длина источника, заполн — заполнитель, длплч — длина полу-
чателя.— Примеч. пер.
351
Рис. 14.5. Преобразование символьной цепочки
вершении выполнения команд MOVTC в регистрах R0—R5 сохра-
няются следующие данные:
в R0 — число непреобразованных байтов, оставшихся, в цепочке-
источнике; .
в R1 — адрес байта, находящегося за последним преобразованным
байтом цепочки-источника;
в R2, R4 — 0; ' х
в R3 — адрес таблицы преобразования;
в R5 — адрес байта, находящегося за последним байтом цепочки-
получателя.
Пример 14.11. Замена строчных букв прописными. Предположим, что необхо-
димо заменить все строчные буквы фрагмента текста прописными. Большинство
символов не изменяется. Для всех элементов таблицы преобразования TABLE,
кроме соответствующих строчным буквам, содержимое TABLE + n есть п. Коды
ASCII строчных букв равны 97—122ю, поэтому область между значениями TABLE +
+ 97 и TABLE+122 инициализируется на коды ASCII прописных букв.
TABLEi .BYTE 0,1,2,3,493,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
.BYTE 21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,‘40
.BYTE 41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60
.BYTE 64,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80
.BYTE 81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96
.ASCII /ABCDEF0HIJKLMN0P0R8TUVWXYZ/
.BYTE 123,124,125,126,127
Отметим’, что длина таблицы составляет 128 байт, в предположении, что в текст
включены только допустимые коды ASCII.
Допустим, цепочка преобразуемого текста начинается в TEXT и значение ее
длины хранится в регистре R8. Осуществим преобразование «на месте»:
MOVTC R8.TEXT, #0, TABLE, R8, TEXT
(Символ-заполнитель здесь не нужен.)
352
Элементы таблицы преобразования в примере 14.11 столь просто
упорядочены, что не имеет смысла печатать все числа, как это сдела-
но в соответствии с директивами .BYTE. Можно, конечно, создать
таблицу с помощью нескольких несложных циклов во время выпол-
нения, однако это неэффективно, поскольку то же самое можно сде-
лать во время ассемблирования. Вне макрокоманд допускается
использовать директиву .REPEAT, рассмотренную в гл. 11 (о ней
будет идти речь в следующем примере).
Таблицу преобразования необходимо создавать во время выпол-
нения, если она зависит от получающихся данных или способа вы-
числения. Так, программа генерирования криптограмм путем раз-
личных перестановок букв может сформировать таблицу, пользуясь
генератором случайных чисел, и заполнить ее во время выполне-
ния.
Пример 14.12. Формирование таблицы с помощью директивы .REPEAT (см.
пример 14.11):
TABLEi .BYTE 0 1 Первый байт таблицы
CODE “ 1
.REPEAT 96 1 For CODE - 1 to 96
.BYTE CODE 1 передать CODE в TABLE+CODE
CODE - CODE+1 >
.ENDR
ASCII /ABCDEFSHIJKLMNOPQRSTUVWXYZ/
CODE - 123
.REPEAT 5 1 For CODE - 123 to 127
.BYTE CODE 1 передать CODE в TABLE+CODE
CODE - CODE+1
.ENDR
Команду MOVTC можно использовать не только для преобра-
зований, о чем свидетельствует приведенный ниже пример.
Пример 14.13. Выделение полей из записей. Предположим, что имеется набор
записей длиной по 200 байт, состоящих из большого числа полей. Для каждой запи-
си требуется выделить данные из нескольких полей и сформировать из выделенных
фрагментов цепочку. Задачу можно решить с помощью команды MOVTC, причем
роль таблицы преобразования играет сама запись. Преобразуемой «щепочкой»
служит шаблон, содержащий позиционные номера байтов, выделяемых из записи.
Выделим 18, 3 и 30-й пятибайтовые фрагменты в предположении, что адреса обра-
батываемых записей загружаются в регистр R9:
TEMPLATEI .BYTE 90,91,92,93,94,15,16,17,18,19,150,151,152,153,154
EXCERPTS .BLKB 15
MOVTC #15,TEMPLATE,#O,(R9>,#15,EXCERPT
12 Зак. 821
353
Команда MOVTUC преобразования с переключением. Команда
MOVTUC (переслать преобразованные символы до обнаружения
символа переключения) преобразует цепочку символов с помощью
таблицы преобразования, как это делается по команде MOVTC,
и, кроме того, инициирует поиск символа. Один из ее операндов на-
зывается символом переключения. Команда MOVTUC останавли-
вает преобразование цепочки-источника, если в ней встречается
символ, который должен быть преобразован в символ переключе-
ния. Формат команды1:
MOVTUC длист, источник, переключ, таблица, длплч, получатель
Преобразование осуществляется по схеме, приведенной на
рис. 14.5, и завершается по достижении в источнике символа, пре-
образующегося в символ переключения, либо по достижении конца
источника или получателя. Если преобразование завершается без
переключения, а получатель длиннее источника, лишние байты в по-
лучателе не изменяются, так как в команде отсутствует операнд-
заполнитель. Программа может определить, произошло ли переклю-
чение и, если оно произошло, то где именно, пользуясь информацией
в регистрах R0—R5 и значениями кодов условий. После выполнения
команды в регистрах сохраняются следующие данные:
в R0 — число байтов, оставшихся в цепочке-источнике, включая
и байт, вызвавший переключение, если оно произошло;
в R1 — адрес байта в источнике, вызвавшего окончание выполнения
команды из-за преобразования в символ переключения либо из-за
превышения длины цепочки-получателя, если выполнение заверши-
лось до преобразования всей цепочки-источника; в противном слу-
чае — адрес байта, находящегося после цепочки-источника;
в R2 — 0;
в R3 — адрес таблицы; \
в R4 — число байтов, оставшихся в цепочке-получателе, включая
и тот, который должен был принять символ переключения, если он
встретился;
в R5 — адрес первого байта цепочки-получателя, который не полу-
чил преобразованного байта источника из-за переключения либо
из-за того, что источник короче получателя; в противном случае —
адрес байта, следующего за цепочкой-получателем.
Код условия V будет установлен, если команда прекращена
из-за переключения, и сброшен, если этого не произошло.
Коды условий N, Z и С показывают отношение между длинами
источника и получателя.
1 Здесть длист — длина источника, переключ — символ переключения, длплч —
длина получателя.— Примеч. пер.
354
Необходимо помнить, что действия команды MOVTUC непред-
сказуемы, если цепочка-получатель перекрывает цепочку-источник
или таблицу преобразования.
Пример 14.14. Преобразование и разделение строк. Пусть BLOCK — адрес
большого блока данных в коде EBCDIC, состоящего из нескольких строк, каждая
из которых заканчивается символом возврата каретки (код 13 в символьных кодах
EBCDIC и ASCII). Необходимо преобразовать код EBCDIC в ASCII и последова-
тельно обработать строки. В реальных ситуациях обработка строк может заклю-
чаться в записи их в новый дисковый файл, но для простоты мы сведем ее здесь
просто к печати строки (если строка длинная, то печатаются только первые 85 сим-
волов). Предполагается, что размер блока данных находится в слове BLK_SZ и
максимальная длина строки во входном блоке равна 150 символам.
Таблица построена таким образом, что элемент в TABLE +/г представляет
собой код ASCII символа, код EBCDIC которого равен п. Например, коды EBCDIC
цифр составляют 240—249, поэтому байты с TABLE+ 240 по TABLE+ 249 содер-
жат 48—57, т. е. коды ASCII цифр. (Некоторые коды EBCDIC не имеют эквива-
лентов в коде ASCII, а ряд кодов не используется; для них элементы таблицы равны
нулю.) Символом переключения служит символ возврата каретки.
CR - 13 $ Возврат каретки
MAX.8Z = 150 1 Максимальный размер входной строки
TABLE: .BYTE 0,1,2,3,0,9,0,0,0,0,0,11,12,13,14,15,16,17,18,0,0
.BYTE 0,8,0,24,25,0,0,0,29,0,31,0,0,28,0,0,10,23,27,0
.BYTE О О О О U 0* ч о о м м о о ы о о о о о о о 8
.BYTE 21,0,26,32,0С93 ,0,46,60,40,43,0,38,0C9ZI
.BYTE 33,36,42,41,59,0,45,47,ОС93,44,37,95,62,63,ОС93,96
.BYTE 58,35,64,39,61,34,0,97,98,99,100,101,102,103,104,105
.BYTE ОС73,106,107,108,109,110,111,112,113,114,ОС73
.BYTE 126,115,116,117,118,119,120,121,122,0С223,123
.BYTE 65,66,67,68,69,70,71,72,73,0С63,125
.BYTE 74,75,76,77,78,79,80,81,82,ОС63,92,0
.BYTE 83,84,85,86,87,88,89,90,ОС63
.BYTE 48,49,50,51,52,53,54,55,56,57,124,ОС53
LINE: .BLKB MAX.8Z
{ИСПОЛЬЗОВАНИЕ РЕГИСТРОВ! R0,R10 длина оставшегося источника
1 R1 адрес возврата каретки в источнике
1 R4 число неиспользуемых байт
5 в получателе
1 R6 длина печатаемой строки
1 Rll адрес оставшегося источника
5
MOVAB BLOCK,R11 1 Адрес цепочки-источника
12*
355
MOVW BLK_8Z,R10 1 Длина цепочки-источника
1 NEXTi MOVTUC RIO,(Rll),#CR, TABLE,#MAX_SZ,LINE | Преобразовать строку
BVC END.OR.ERROR 1 CR не найден
8UBW3 R4,#MAX_SZ,R6 1 Вычислить раомер строки
SUBL3 #1,RO,RIO 1 Длина оставшегося источника
ADDL3 #1,R1,R11 1 Адрес оставшегося источника
PRINTCHRS LINE,R6 1 Напечатать строку
BRB NEXT 1 Воять другую строку
END.OR.ERRORs T8TW RIO 1 Остались еще символы?
BNEQ ERROR
END। <обработка оаворшона>
Отметим, что если бы макрокоманда PRINTCHRS не использовала регистры
R0 и R1, то регистры R10 и R11 оказались бы ненужными, а сама программа не-
сколько упростилась.
14.5. Команда редактирования
В гл. 6 было показано, каким образом можно преобразовать
дополнительный код, в котором представлены данные, в символьный
для их вывода, пользуясь командами CVTLP и CVTPS. Результа-
том преобразования является цепочка в символьном коде, составлен-
ная из цифр с предшествующим им знаком. Если преобразуемое
число имеет меньше значащих цифр, чем определенное в команде
CVTPS количество разрядов, первые несколько цифр в результате
окажутся нулевыми. Если, например, содержимое регистра R7
составляет FFFFFEBC, то после выполнения команд
CVTLP R7, #5; RKD
CVTPS #5,RKD, #5,LSN
имеем:
PKD и затем LSN
00 30 8D 2D 30 30 33 30 38
Отметим, что здесь и на всех остальных диаграммах содержимого
памяти в настоящем разделе первый байт (с наименьшим адресом)
показан слева. При печати результата из LSN получается число
—00308. Во многих ситуациях более предпочтителен вывод в форме
-308.
Пользуясь ранее рассмотренными командами, можно проверить
цепочку в LSN на наличие нулей, заменить их пробелами и передви-
нуть знак. Такое форматирование представляется довольно громозд-
ким и требует применения множества команд.
В системе команд некоторых болыЬих ЭВМ предусмотрена
команда EDIT, предназначенная для форматирования численных
356
данных для вывода. Она преобразует число в упакованном десятич-
ном формате в символьную цепочку под управлением определяюще-
го формат шаблона. Эта команда используется компиляторами для
форматирования, задаваемого оператором FORMAT языка Фортран
или спецификацией изображения в языках PL/1, Кобол и т. д. Зада-
чи, реализуемые с помощью команды EDIT, различаются в деталях
в разных ЭВМ, но обычно включают в себя введение пробелов вместо
нулей старших разрядов, размещение знака Перед первой значащей
цифрой, введение десятичной точки, знака доллара и других спе-
циальных символов.
Команда EDIT в системе VAX имеет следующий формат:
EDITPC число_цифр, упк, шаблон, получатель
Первые два операнда описывают подлежащее редактированию ,
упакованное десятичное число. Шаблон представляет собой цепочку
байтов в памяти, содержащую «операторы шаблона редактирова-
ния»— последовательность команд редактирования. Большинство
операторов размещают один или несколько байтов, в которых на-
ходятся цифры, символ-заполнитель, знак или специальный символ,
в следующем доступном байте (или байтах) получателя. Операнд
длины шаблона в команде EDITPC отсутствует; конец шаблона
отмечается оператором «конец шаблона»— нулевым байтом. Опе-
ранд длины получателя также не задается, так как шаблон полно-
стью определяет длину редактируемой символьной цепочки.
Необходимо помнить, что команда EDITPC при использовании
регистров R0—R5 в качестве рабочих разрушает их исходное со-
держимое.
Существуют 16 различных операторов шаблона. Для некоторых
из них требуются операнды. Прежде чем перейти к описанию опе-
раций, реализуемых операторами, и кодирования операторов в ша-
блоне, приведем несколько примеров, в которых обосновывается
применение тех или иных операторов и иллюстрируются возмож-
ности команды EDIT. Операторам присваиваются символические
имена, начинающиеся в ЕО$ (Edit Operator — оператор редакти-
рования). Эти имена фактически являются системными макрокоман-
дами, инициализирующими байты шаблона, но здесь мы рассматри-
ваем их только как абстрактные имена операторов шаблона.
Пример 14.15. Простой шаблон для редактирования положительных целых
чисел. Предположим, что PKD содержит целое число в упакованном десятичном
формате, которое, как нам известно, положительно и состоит из пяти цифр. Тре-
буется преобразовать формат этого числа в символьный код с заменой нулей стар-
ших разрядов пробелами. По команде EDITPC цифры источника обрабатываются
слева направо. Если следующая обрабатываемая цифра в упакованном числе рав-
на нулю и до этого ненулевых цифр не было, необходимо в следующий байт полу-
чателя добавить сим вол-заполнитель (обычно пробел). Если же обрабатываемая
цифра не равна нулю (или равна нулю, но ранее уже встречались ненулевые цифры),
ее представление нужно преобразовать в символьный код ASCII и запомнить эту
цифру в следующем байте получателя. Оператор EO$MOVE действует именно
таким образом. Ему требуется операнд, называемый счетчиком повторений, который
357
определяет число обрабатываемых цифр. Оператором конца шаблона служит
EO$END. Таким образом, получаем следующие команды:
EO$MOVE 5
EO$END
Если, например, PKD содержит 00 70 2С и шаблон правильно закодирован
в памяти по адресу PATRN, то по команде
EDITPC #5, RKD, PATRN, STRING
в памяти, начиная с адреса STRING, будет сформирована следующая цепочка:
20 20 37 30 32
В этой цепочке первые два байта содержат коды пробела.
Читателю рекомендуется самостоятельно выяснить, почему такой шаблон не
применим, если редактируемое целое число равно нулю.
Пример 14.16. Шаблон для редактирования знаковых целых чисел. Будем по-
прежнему считать, что редактируемое целое число состоит из пяти цифр, но о знаке
его нам ничего не известно: это число может быть отрицательным, положительным
или нулевым. Описываемый ниже шаблон редактирования заполняет пробелами
нули старших разрядов и помещает знак перед первой значащей цифрой. Для
обработки первых четырех цифр воспользуемся оператором ЕО$FLOAT. Как и
оператор EOSMOVE, он заменяет нули старших разрядов пробелами, но дополни-
тельно заставляет знак сплыть» (float) над пробелами. Когда в упакованном
числе встречается ненулевая цифра, оператор помещает знак и эту цифру (расши-
ренные из четырехбитового представления в соответствующие символьные коды
ASCII) в следующие два байта получателя. В дальнейшем все встречаемые опера-
тором EO$FLOAT цифры преобразуются в символьный код и запоминаются в по-
лучателе. Пятую цифру нельзя заменять пробелом, даже если она равна нулю.
Если редактируемое целое число равно нулю, при выводе должна появиться одна
нулевая цифра. Оператор EO$END___FLOAT устанавливает флаг состояния, пока-
зывающий, что все последующие цифры считаются значащими и не должны заме-
няться символом-заполнителем. В получателе запоминается символ знака, если до
этого знак -не был запомнен. Оператор EOSMOVE помещает в получатель код
ASCII последней цифры. Таким образом, наш шаблон редактирования имеет вид:/
EO6FLOAT 4
EO#END_FLOAT
EOBMOVE 1
EOftEND
Отметим, что для отредактированного результата необходимо зарезервиро-
вать шесть байтов: пять для цифр или заполнителя и один для знака, который вво-
дится оператором EO$FLOAT или EO$END_____FLOAT.
На рис. 14.6 представлены этапы редактирования, реализуемые командой
EDITPC #5, PKD, PATRN, STRING
в предположении, что шаблон редактирования правильно закодирован в памяти,
начиная с PATRN. Показанные на рисунке флаг значимости, символ-заполнитель
и символ знака более подробно описаны ниже.
Флаг значимости. Примеры 14.15 и 14.16 показывают необходи-
мость наличия общего для команд редактирования атрибута —
флага значимости. При обработке цифры 0 оператор EO$MOVE
или EO$FLOAT должен либо разместить ее в получателе, либо
заменить символом-заполнителем. Когда оператор EO$FLOAT
встречает ненулевую цифру, он должен определить, запомнить ли
только цифру или цифру вместе со знаком. В любом случае необхо-
димо выбрать первый вариант, если оператору ранее попалась
хотя бы одна значащая (т. е. ненулевая) цифра. Команда EDITPC
системы VAX использует в качестве флага значимости бит кода
358
Заполнитель =ЛХ20 Знак = AX2D
Оператор Упакованный источник Получатель
EO$FLOAT 00 30 8D 20
EO$FLOAT 00 30 8D 20 20
EO$FLOAT 00 30 8D 20 20 2D 33 знак цифра Флажок значимости установлен
ЕО$FLOAT 00 30 8D 20 20 2D 33 30
EO$END_FLOAT Нет действия, так как флажок значимости уже установлен
EOfcMOVE EO$END 00 30 8D a) 20 20 2D 33 30 38
• Заполнитель = АХ20 Знак = АХ20
Оператор Упакованный источник Получатель
EO$FLOAT 00 00 4C 20
ЕО$FLOAT ЕО$FLOAT 00 00 4C 00 00 4C 20 20 20 20 20
ЕО$FLOAT 00 00 4C EO$END_FLOAT 20 20 20 20 20 20 20 20 20 знак Флажок значимости установлен
EO$MOVE EO$END 00 00 4C 6) 20 20 20 20 20 34
Рис. 14.6. Этапы редактирования упакованных чисел:
а) 00 30 8D, б) 00 00 4С
условия С. Он сбрасывается, как тольно начинается выполнение
команды EDITPC, и автоматически устанавливается операторами
EO$MOVE и EO$FLOAT при появлении ненулевой цифры. Другие
операторы так же привлекают флаг значимости.
Символ-заполнитель и символ знака. Как следует из примеров
14.15 и 14.16, по умолчанию в качестве значения символа-заполни-
теля (символа, который заменяет нули старших разрядов) прини-
мается пробел. Аналогично значениями, принятыми по умолчанию,
для знака являются пробел при положительных числах и символ «—»
при отрицательных (знак определяется последней тетрадой упако-
ванного исходного данного). Значения, принятые по умолчанию,
можно изменить операторами шаблона. Команда EDITPC инициа-
лизирует регистр заполнителя и регистр знака (оба они фактически
входят в регистр R2 как его составные части) на значения по умол-
чанию. С помощью нескольких операторов программист может при
редактировании загрузить в эти регистры другие символы. Ниже
будут приведены примеры таких операторов.
Представление операторов шаблона. Шаблон редактирования
представляет собой цепочку операторов (в некоторых случаях
вместе с операндами). Различают три вида операндов: счетчики
повторений (см. примеры 14.15 и 14.16 — операторы EO$FLOAT
и EO$MOVE), операнд символа (других символа-заполнителя и
знака или специальных символов, вводимых в выходную цепочку)
и операнд длины. В тех операторах, где используется счетчик повто-
359
рений (число, показывающее, сколько раз применяется оператор),
собственно оператор и счетчик повторений занимают один байт.
Для оператора отведены биты 7:4, а для счетчика повторений —
биты 3:5; следовательно, число повторений составляет 1—15. Опе-
раторы, не требующие операнда либо имеющие операнд символа
или длины, занимают один байт; операнд, если он есть, находится в
следующем байте.
В табл. 14.1 (в алфавитном порядке) приведены все операторы
шаблонов, их коды и выполняемые ими операции. Имя каждого
оператора является также именем системной макрокоманды, кото-
рая генерирует одно- или двухбайтовый код оператора и его опе-
ранда, если он имеется. Хотя шаблон можно инициализировать
в одной строке, пользуясь директивой .BYTE и кодом из табл. 14.1, с
применением имен определения шаблонов становятся более понят-
ными и сокращается число ошибок.
Пример 14.17. Пользуясь табл. 14.1, можно определить, что команда, рассмотрен-
ная в примере 14.16, должна использовать следующий шаблон:
PATRN
А4 01 91 00
Инициализация шаблона в программе:
PATRN» EOBFLOAT 4
EOPEND.FLOAT
EOSMOVE 1
EMEND
Примеры. Как руководство к заданию шаблонов в приводимых
ниже примерах мы будем выписывать форматные диаграммы ожи-
даемого результата. Эти диаграммы показывают, сколько цифр
обрабатывается, куда помещаются специальные символы, нужно ли
запоминать знак, где необходимо включить флаг значимости и т. д.
Одно из достоинств такой диаграммы заключается в том, что она
помогает определить, сколько символов должен содержать отредак-
тированный результат, т. е. сколько байтов необходимо зарезерви-
ровать для операнда-получателя в команде EDITPC. Буквой d
обозначена цифра или символ-заполнитель, а буквой s — знак.
Последняя показывается в первой позиции, где может появиться
знак; в большинстве примеров знак «плавает» над старшими сим-
волами-заполнителями и появляется перед первой значащей циф-
рой. Символ вставки указывает позицию, где должен быть установ-
лен флаг значимости, если он не был установлен ранее.
Форматная диаграмма для простого шаблона из примера 14.15
имеет вид:
d d d d d
Диаграмма для шаблона из примера 14.16 несколько сложнее:
s d d d dAd
Пример 14.18. Введение специальных символов. Необходимо определить шаблон
360
для редактирования семиразрядного целого числа с запятыми после разрядов мил-
лионов и тысяч. Форматная диаграмма имеет вид:
s d, ddd, dd^d
Очевидно, появление запятой нежелательно, если слева от нее нет ни одной
значащей цифры. Оператор EO$INSERT предназначен для запоминания символа-
заполнителя, а не символа, определенного его операндом, если флаг значимости не
установлен. Отметим, что для получателя требуется 10 байт. Шаблон формируется
следующим образом:
PATRN: EO®FLOAT 1
ЕОЛINSERT <,>
EOBFLOAT 3
ED®INSERT <,>
E08FL0AT 2
EOBEND_FLOAT
EOBMOVE 1
EOBEND
Подчеркнем, что запятую необходимо заключать в угловые скобки: при отсут-
ствии угловых скобок ассемблер интерпретирует запятую как разделитель и генери-
рует сообщение об ошибке.
Кодирование шаблона:
PATRN
Al 44 2С АЗ 44 2С А2 01 91 00
Пример 14.19. Другой символ-заполнитель. Требуется напечатать положитель-
ное пятиразрядное число с тремя звездочками до и после него, а также со звездоч-
ками вместо нулей старших разрядов. Форматная диаграмма принимает вид:
♦ * * ♦
* * * ddddAd***
Здесь над цифрами d показан другой символ-заполнитель, так как им не будет
обычный пробел. Шаблон инициализируется следующим образом:
PATRN: EOBLOAD_FILL #
ЕDBFILL 3
EOBMOVE 4
EO1SET_SI0NIF
E08M0VE 1
ED®FILL 3
E09END
Пример 14.20. «Плавающий» символ $ вместо знака. Предположим, что редак-
тируется семиразрядное число, представляющее собой денежную сумму. Перед
двумя правыми цифрами должна находиться десятичная точка, а перед первой
ненулевой цифрой — знак доллара. Знак доллара загружается в регистр знака
(числа) и вводится в соответствующее место оператором EO$FLOAT или
EO$END____FLOAT. Необходимо предусмотреть следующий специальный случай:
если сумма меньше доллара, десятичная точка не появится, так как ей не предшест-
вует значащая цифра. Прэтому перед десятичной точкой необходимо установить
флаг значимости. Мы установим его так, чтобы перед десятичной точкой была
минимум одна цифра, возможно даже нуль. Таким образом, мы получаем форматную
диаграмму:
J dd, ddAd.dd
Шаблон инициализируется операторами:
PATRN: E09L0AD.8ISN «
EOBFLOAT 2
361
EO#INSERT <,>
EOtFLOAT 2
EOtEND.FLOAT
EOtMOVE 1
ED#INSERT
EOtMOVE 2
EOftEND
Пусть, например, содержимое PKD составляет
00 00 00 9C
Тогда по команде
EDITPC #7, PKD, PATRN, AMOUNT
в памяти начиная с адреса AMOUNT разместятся следующие данные:
20 20 20 20 20 24 30 2Е 30 39
При печати эти данные будут выглядеть как 30.09 (перед знаком доллара имеется
пять пробелов).
Пример 14.21. Коррекция длины источника. Предположим, что необходимо
сформировать четырехразрядное число. Упакованное десятичное число всегда
имеет нечетное число разрядов. Оператор ЕО$ADJUST___INPUT заменяет длину
источника своим операндом. Если, например, упакованное число имеет пять раз-
рядов, оператор EO$ADJUST__INPUT с операндом 4 заставит указатель источника
в команде EDITPC продвинуться на одну тетраду, пропустив первую цифру. Приво-
димый ниже шаблон формирует беззнаковое четырехразрядное целое число с вклю-
ченными нулями старших разрядов, которые не заменяются символом-заполните-
лем. Форматная диаграмма имеет вид:
дб d d d
Шаблон инициализируется операторами:
PATRNi EDftADJUST,INPUT 4
EOeSET_SIGNIF
E06M0VE 4
EQftENB
Коды условий. Команда EDITPC воздействует на все коды усло-
вий. Как упоминалось выше, флаг С используется в качестве флага
значимости. Биты N и Z имеют свой обычный смысл. Флаг N уста-
навливается при отрицательном знаке упакованного операнда-
источника, однако этот флаг будет сброшен, если все цифры источни-
ка равны нулю, т. е. если упакованный операнд равен —0. Флаг Z
устанавливается, когда все цифры источника равны нулю, а флаг
V (фиксирует переполнение)— когда оператор EO3ADJUST INPUT
вызывает уничтожение хотя бы одной ненулевой цифры. Перепол-
нение может возникнуть только в этой единственной ситуации.
Если для получения в памяти не зарезервировано достаточно места,
по команде EDITPC «перезаписываются» другие данные.
Конечно, применение такой команды, как EDITPC, реализующей
множество различных операций, связано с рядом проблем. Боль-
шинство ситуаций, в которых возникают ошибки, вполне объяснимо.
Как правило, ошибка зарезервированного операнда возникает в
том случае, если число цифр, определенных для упакованного дан-
ного-источника оказывается вне диапазона 0—31. Определяемые
362
для оператора EO$ADJUST______INPUT значения длины также дол-
жны находиться в этом диапазоне. (Если упакованное число содер-
жит запрещенную тетраду, результат будет неправильным, но ошиб-
ка может не возникнуть.) Ошибка зарезервированного операнда
возникает и тогда, когда шаблон не обрабатывает все цифры в источ-
нике или когда он пытается обработать больше цифр, чем их есть на
самом деле, а также при недействительном коде оператора р шабло-
не.
Необходимо помнить о том, что результаты действия команды
EDITPC непредсказуемы, если получатель перекрывает упакован-
ный операнд-источник или шаблон.
14.6. Заключение
В ЭВМ семейства VAX имеется система команд, реализующих
операции над символьными цепочками. Эти команды весьма удобны
для обработки входных и выходных данных, редактирования текстов
и т. д. Символьные цепочки определяются в машинных командах
своей длиной и адресом первого байта. В качестве рабочих исполь-
зуются некоторые общие регистры, с помощью которых программе
передается информация о результатах выполненной операции.
Наиболее сложной из этих команд является EDITPC, предназ-
наченная для редактирования упакованных десятичных чисел в со-
ответствии с шаблоном, определенным как один из ее операндов.
Командой EDITPC можно воспользоваться для включения в резуль-
тат специальных символов, «гашения» пробелами нулей старших
разрядов и введения знака (или другого символа) перед первой
значащей цифрой. Операторы шаблона редактирования приведены
в табл. 14.1.
Рассмотренные в настоящей главе команды сведены в табл. 14.2.
14.7. Упражнения
1. Предположим, что, если элемент слишком велик для третьего столбца табли-
цы (см. примеры 14.1 и 14.3), он полностью печатается в следующей строке с пози-
ции, в которой начинается этот столбец. Пусть элемент состоит максимум из 40 сим-
волов. Напишите команды для копирования цепочки в нужное место и «гашения*
пробелами всех требуемых байтов.
2. Какие данные останутся в регистрах RO, R1 и R3 после выполнения команды
MOVC5 из примера 14.2? Какие значения будут иметь коды условий?
3. Проанализируйте символьную цепочку и программный сегмент в примере
14.5. Какие данные остаются в регистре R0 после каждого выполнения команды
LOCC? Почему первоначально в регистр R1 загружается значение по адресу
ТЕХТ+1, а не по адресу TEXT?
4. Перепишите команды цикла в программном сегменте из примера 14.5 таким
образом, чтобы одну команду из цикла можно было удалить. (Допускаются любые
изменения в инициализации, но при этом сегмент должен все-таки выполняться
правильно.)
363
Операторы шаблона редактирования
Таблица 14.1
Оператор Код Операнд Операция
EO$ADJUST_INPUT EO$BLANK_ZERO EO$CLEAR_SIGNIF EOS'END EO$END_FLOAT EOS FILL EOSFLOAT EOSINSERT EO$LOAD_FILL EOSLOAD MINUS EOSLOAD PLUS EOSLOAD SIGN EOSMOVE EOSREPLACE SIGN EOSSET SIGFJIF EOSSTORE SIGN 47 45 02 00 01 80+r А0+/ 44 40 43 42 41 90+ r 46 03 04 длина длина счетчик повторений счетчик повторений символ символ символ символ символ счетчик длина Определить число используемых разрядов (цифр) источника, заменяя операнд число цифр. По мере необходимости до- полнительные нули старших разрядов в источнике либо игно-. рируются, либо создаются Если операнд-источник равен нулю, передать символ-запол- нитель в последние.байты получателя, определяемые длиной Сбросить флаг значимости Закончить шаблон Если флаг значимости сброшен, установить его и запомнить знак в получателе Запомнить в получателе г копий символа-заполнителя Если флаг значимости установлен, запомнить следующую цифру Если же флаг значимости сброшен и цифра — нуль, запом- нить сим вол-заполнитель; в противном случае запомнить знак и цифру и установить флаг значимости Если флаг значимости установлен, запомнить символ, в противном случае запомнить заполнитель Передать символ в регистр заполнителя Передать символ в регистр знака, если источник меньше нуля Передать символ в регистр знака, если источник больше нуля Передать символ в регистр знака Если флаг значимости установлен, запомнить следующую цифру. Если же флаг значимости сброшен и цифра равна нулю, запомнить заполнитель; в противном случае запомнить цифру и установить флаг значимости Если источник равен —0, заменить знак на заполнитель Установить флаг значимости Запомнить знак в получателе
Команды, реализующие операции над символьным цепочками
Таблица 14.2
Команда Операция Регистры
MOVC3 MOVC5 СМРСЗ СМРС5 LOCC SKPC SCANC SPANC МАТСНС MOVTC MOVTUC EDITPC t длина, источник, получатель длист, ист, заполнитель, длплч, плч длина, цепочка!, цепочка2 дл1, цеп!, заполнитель, дл2, цеп2 символ, длина, цепочка символ, длина, цепочка длина, цепочка, таблица, маска длина цепочка, таблица, маска длшабл, шаблон, длцеп, цепочка длист, ист, заполн, таблица, длплч, плч длист,ист,перекл,таблиц а,длплч,плч чц, упк, шаблон, получатель Скопировать цепочку Скопировать цепочку Сравнить цепочки Сравнить цепочки Отыскать первый байт, содержащий символ Отыскать первый байт, не содержащий символ Отыскать первый байт, у которого элемент таблицы AND (маска =#0 (SCANC) или =0(SPANC) Отыскать шаблон в цепочке Преобразовать источник Преобразовать с переключением Отредактировать упакованное данное RO—R5 RO—R5 RO—R3 RO—R3 R0-R1 RO—R1 RO—R3 RO—R3 RO— R5 RO— R5 RO—R5
5. Напишите процедуру, реализующую операцию по команде SKPC, не поль-
зуясь введенными в настоящей главе командами. Считайте CHAR, LEN и STRING
аргументами, используемыми в качестве операндов символ, длина и цепочка. Про-
цедура должна возвращать в RO, R1 и Z те же данные, которые остаются в них
после выполнения команды SKPC.
6. Напишите процедуры для подсчета каждой из гласных, встречаемых опе-
ратором в текстовом блоке. Аргументами процедуры являются TEXT, LENGTH
(число байтов в тексте) и массив VOWELS слов, в котором должны запоминаться
результаты. (Не рекомендуется явно проверять, какая гласная обнаружена. По-
пробуйте найти способ «автоматического» выполнения инкремента счетчика.)
7. Модифицируйте процедуру CVTCL, показанную на рис. 14.2, таким образом,
чтобы она правильно выполнялась даже в том случае, когда символьный код одного
знакового числа следует непосредственно за символьным кодом другого числа без
ра зд ел и тел я - п р обел а.
8. Напишите процедуру REPLACE, которая находит и заменяет первую встре-
тившуюся подцепочку в цепочке другой подцепочкой. Процедура REPLACE должна
иметь шесть аргументов:
STRING — модифицируемая цепочка;
STR__LEN — ее длина;
SUB___STR___1 — заменяемая подцепочка;
STR___1__LEN — ее длина;
SUB . STR___2 — заменяющая цепочка;
STR___2__LEN — ее длина.
Значение STR_JLEN необходимо заменить на новое значение длины модифици-
рованной цепочки. Считайте, что за последним байтом цепочки остается достаточно
места для «растяжения» ее до общей длины в 150 символов. Если в результате за-
мены длина цепочки превысит 150 символов, ее выполнять не нужно. Процедура
REPLACE должна устанавливать флаг в регистре R0, фиксирующий три случая:
успешная замена, заменяемая подцепочка не найдена, длина превышает заданный
предел. В документации поясните, какие значения в регистре R0 соответствуют
каждому случаю.
9. Напишите программный сегмент, который находит в таблице с форматом,
указанным в примере 14.10, первый элемент с ключом, равным содержимому сло-
ва KEY.
10. Разработайте алгоритм для выполнения операции по команде МАТСНС
(для прове’рки, не входит ли одна цепочка в другую), если заданы адреса и длины
двух цепочек. Алгоритм должен присваивать переменной значение адреса, по кото-
рому шаблон появляется во второй цепочке, или нуль, если он не обнаружен.
11. Учитывая оставшиеся в регистрах после завершения команды MOVTC
данные, попытайтесь определить, что следует загрузить в регистры RO, R1 и R5
перед выполнением этой команды.
12. Напишите процедуру, реализующую операцию по команде MOVTC, не поль-
зуясь ни одной из рассмотренных в настоящей главе команд. Считайте, что аргумен-
ты SRC____LEN, SOURCE, FILL, TABLE, DEST_____LEN и DEST соответствуют опе-
рандам команды MOVTC. Процедура должна оставлять в регистрах RO—R5 те
же данные, что и команда MOVTC.
13. Является ли спецификатор второго операнда команды
MOVB #аа//, table+aa/o/
допустимым? Ответ обоснуйте. Каковы функции этой команды?
14. Напишите ассемблерные директивы для построения таблицы преобразова-
ния, которую можно использовать при замене квадратных скобок фигурными, сохра-
няя неизменными все остальные символы.
15. Построенная в примере 14.11 таблица преобразования содержит только
12§ байт. Какие ошибки могут возникнуть, если один из байтов преобразуемой
цепочки содержит число, большее 127?
16. Напишите команды для выполнения операций, описанных в примере 14.13,
не пользуясь командой MOVTC.
366
17. Напишите программу для последовательного считывания строк с терминала,
преобразования их с применением таблицы. Эта программа должна переставлять
буквы, но сохранять знаки пунктуации, и печатать полученную криптограмму.
Предусмотрите в ней процедуру, случайным образом формирующую подстановку.
18. Выполните упр. 42 гл. 9, пользуясь командой MOVTUC.
19. Приведите коды шаблона редактирования из примера 14.15.
20. Если содержимое PKD составляет 00 00 ОС и используются команда
EDITPC и шаблон из примера 14.15, что окажется в байтах,начиная со STRING,
после завершения команды?
21. Будет ли шаблон, генерируемый представленными ниже операторами,
действовать так же, как шаблон из примера 14.16? Если да, то поясните почему.
В противном случае приведите пример цеп очки-источника, для которой шаблоны
дают различные результаты.
PATRNs ЕОAFLOAT 4
EOftSET.SIGNIF
EO9MOVE 1
EOftEND
22. Приведите коды шаблона из примера 14.19. Сколько байтов необходимо
зарезервировать для отредактированного результата?
23. Покажите содержимое байтов начиная с AMOUNT при использовании
команд EDITPC и шаблона из примера 14.20, если в области PKD было
04 89 00 1С.
24. Напишите последовательность макрокоманд инициализации шаблона, пред-
назначенного для редактирования знакового шестиразрядного целого числа.
25. Напишите процедуру, которая преобразует элементы массива длинных слов
в символьные коды (с помощью команды EDITPC) и печатает их по восемь чисел
в строке. Считайте, что элементы массива имеют максимум шесть значащих цифр.
Выходные данные должны быть выравнены по столбцам, содержащим 10 позиций
на элемент. Аргументами процедуры являются массив и длинное слово с числом
элементов в массиве.
26. Напишите последовательность макрокоманд инициализации шаблона для
вывода в формате, указанном в примере 14.19, но с буквами CR за последней цифрой
числа, если оно отрицательное.
27. Напишите последовательность макрокоманд инициализации шаблона для
вывода в формате, указанном в примере 14.20, но со звездочкой в качестве символа-
заполнителя.
28. Напишите последовательность макрокоманд инициализации шаблона для
редактирования трехразрядного числа (если число отрицательное, за его последней
цифрой появляется знак <—»).
29. Напишите последовательность макрокоманд инициализации шаблона для
редактирования пятиразрядного знакового целого числа, пользуясь в качестве
заполнителя пробелом, но знак должен появиться в первом выводимом байте.
30. Напишите процедуру (не применяя команду EDITPC), которая восприни-
мает пятиразрядную упакованную десятичную цепочку и преобразует ее в шести-
символьную цепочку, где нули старших разрядов заменены пробелами, а знак по-
является точно перед первой значащей цифрой.
Глава 15
Ввод и вывод
с использованием RMS
15.1. Операции ввода-вывода
Операции ввода-вывода считаются наиболее сложными в прог-
рамме, что объясняется несколькими причинами. Центральный
процессор и оперативное запоминающее устройство (основная па-
мять) ЭВМ размещены в стойке и взаимодействуют друг с другом с
очень высокой скоростью. Пока операции ввода-вывода не выпол-
няются, процессор действует самостоятельно, обрабатывая и пересы-
лая данные в памяти. Однако при выполнении операции ввода-вы-
вода процессору приходится взаимодействовать с физическими
устройствами, отличающимися от него по своему быстродействию
и другим характеристикам. Среди факторов, усложняющих прог-
раммирование ввода-вывода, отметим следующие:
1. Физические устройства ввода-вывода работают гораздо мед-
леннее, чем центральный процессор.
2. Во избежание потери времени процессор выполняет часть
другой программы, пока занятая вводом-выводом программа ожи-
дает завершения операции. Действия процессора и программы,
выполняющей операции ввода-вывода, оказываются асинхронными,
т. е. естественной координации во времени между ними нет. Поэто-
му, когда медленная операция ввода-вывода завершается, процессор
прерывает выполнение некоторой программы, чтобы соответствую-
щим образом отреагировать на это событие, а затем возобновляет
свою работу.
3. В программах ввода-вывода необходимо предусмотреть такие
ситуации, как отключение устройства ввода-вывода, отсутствие
бумаги в принтере и выключение ленточного накопителя.
В большой многопользовательской системе прикладные програм-
мы не имеют прямого управления вводом-выводом. Это позволяет,
во-первых, освободить программиста от детального изучения опе-
раций ввода-вывода и, во-вторых (что еще более важно), намного
368
эффективнее (и без конфликтов между пользователями) использо-
вать ресурсы — ими управляет операционная система. Следова-
тельно, команды в программе (написанной на Ассемблере или на
языке высокого уровня) считывания или записи данных фактически
являются запросами к операционной системе на выполнение опера-
ции. Более подробную информацию читатель может почерпнуть из
учебников по операционным системам или в соответствующих руко-
водствах для конкретной ЭВМ. С описанием операционной системы
VAX/VMS и операций ввода-вывода можно ознакомиться в руко-
водстве VAX Software Handbook.
Программист может разрабатывать программу в терминах
логических файлов и логических операций ввода-вывода, а не физи-
ческих операций. Операционная система следит за тем, как и где
хранятся фактические данные, и преобразует запрошенную логи-
ческую операцию (например, ввод записи) в физическую, которую
необходимо выполнить для поиска и передачи требуемых данных.
В настоящей главе операции ввода-вывода рассматриваются на
высоком, или логическом, уровне. Читатель, не знакомый с. вводом-
выводом, получит представление об этих операциях, а также о служ-
бе управления записями (RMS), которое обеспечивается операцион-
ной системой. В разд. 15.2 показано, как программист может поль-
зоваться RMS для управления файлами. Конечно, приводимый
здесь материал является далеко не полным, но тем не менее пред-
ставляют интерес некоторые детали ввода-вывода с дисковых фай-
лов и с терминала при непосредственном использовании RMS, а не
косвенных операций с помощью макрокоманд из гл. 5.
15.2. Служба
управления записями
Служба.управления записями VAX-11 представляет собой сово-
купность макрокоманд и процедур, которые дают возможность
пользователю обрабатывать записи и файлы и, в частности, осу-
ществлять ввод и вывод, описывая логическую, а не физическую
организацию записей и файлов и пользуясь операциями сравнитель-
но высокого уровня. Мы рассмотрим RMS лишь частично, так
как ее подробное описание приводится в руководстве VAX-11 Record
Management Services Reference Manual.
Как известно, файл — это организованный набор взаимосвя-
занных данных, называемых записями. В текстовом файле (напри-
мер, в исходном файле программы или в файле данных, таком, как
DATA.DAT в макрокоманде READRCRD) каждая строка образует
запись.
Файлы обычно хранятся на ленте или диске, хотя программа
может построить входной и выходной файлы для терминального
ввода-вывода. Ниже рассматриваются средства RMS, предназна-
. 369
ценные для обработки относительно простых дисковых и терми-
нальных файлов, а также макрокоманды и подпрограммы ввода-
вывода в модулях IOMAC и IOMOD.
Существует несколько способов организации файлов. Простей-
шим способом, которым мы пользовались при организации файла
DATA.DAT и терминального ввода-вывода, считается последова-
тельный. Записи в таком файле размещены в определенной последо-
вательности и обрабатываются по порядку. Кроме последователь-
ного, известны индексный и относительный способы организации
файлов. В индексном файле каждая запись имеет идентификатор,
или ключ, с помощью которого она специфицируется. В относитель-
ном файле записи назначаются конкретным позициям и обращения
к ним осуществляются по номеру позиции.
Взаимодействие с RMS. При обработке файла программа вза-
имодействует с RMS. Для обеспечения их взаимодействия необхо-
-димо выполнить два условия:
1. Программа .должна содержать блоки данных (называемые
управляющими блоками), которые описыврют обрабатываемые
файлы и записи. С помощью управляющих блоков организуется
двустороннее взаимодействие: программа помещает в некоторые
поля блбков информацию для RMS, a RMS загружает в некоторые
поля информацию, возвращаемую программе.
2. Программа должна выдавать конкретные запросы на выпол-
нение операций (например, считать запись) всякий раз, когда это
требуется.
В RMS предусмотрены макрокоманды, применение которых
упрощает резервирование и инициализацию управляющих блоков,
а также обработку файлов и записей. Итак, перейдем к рассмотре-
нию управляющих блоков, но прежде познакомим читателя с терми-
нологией, которую он должен знать, если ему предстоит иметь дело
с файлами и записями. Ниже приводятся наиболее часто употреб-
ляемые термины и раскрывается их смысл:
• открыть файл — проверить, что файл существует, и сделать
его доступным для обработки;
• создать файл — инициализировать элемент каталога и открыть
файл;
• ввести запись — ввести запись из файла;
• вывести запись — вывести запись в файл;
• закрыть файл — закончить обработку файла.
Существует четыре вида управляющих блоков: блоки доступа
к файлам, блоки доступа к записям и два вида блоков для получе-
ния дополнительной информации, которые здесь не описываются,
так как для простого ввода-вывода они не нужны.
Блоки доступа к файлам. Блок доступа к файлу (FAB) состоит
из 80 байтов, содержащих описание конкретного файла. Для любого
файла, к которому обращается программа, необходим один FAB.
370
Блок доступа к файлу включает 28 полей, каждое из которых содер-
жит определенную информацию о файле.
Резервирование пространства для FAB и инициализация' неко-
торых его полей (не всех) осуществляется по макрокоманде
$FAB. Когда файл открывается или создается, его отдельные поля
заполняются RMS. Макрокоманда 3FAB имеет 23 аргумента, мно-
гие из которых точно соответствуют полям в FAB (число аргумен-
тов меньше, чем число полей, поскольку не все поля могут инициали-
зироваться пользователем, а некоторые аргументы инициализируют
несколько полей). Так как ряд полей заполняется RMS и для боль-
шинства аргументов приняты по умолчанию очень удобные для
программиста значения, фактически приходится определять незна-
чительное число аргументов, если, конечно, файл — не специфи-
ческий.
Следует подчеркнуть, что макрокоманда 5FAB инициализирует
область данных и ее необходимо размещать в разделе резервирова-
ния памяти и инициализации программы, а не среди исполнимых
команд.
Формат макрокоманды 3FAB:
метка: $FAB аргументы
Так как в макрокоманде довольно много аргументов и большин-
ство из них обычно опускается, целесообразно определять их с
помощью ключевых слов. Перечислим некоторые аргументы $FAB:
FNM — имя файла. Спецификация файла, описываемого конкрет-
ным FAB. Заключается в угловые скобки.
ORG — организация файла. Вариантами этого аргумента служат:
SEQ (последовательный), INDX (индексный) и REL (относитель-
ный). По умолчанию принимается значение SEQ. Для создаваемого
файла это поле инициализируется программистом. Если же файл
существует, то когда он открывается, RMS загружает в это поле
фактическое значение аргумента.
ALQ — объем распределения. Число дисковых блоков, распреде-
ляемых файлу при его создании. Если файл уже существует, RMS
загружает в это поле фактический размер файла в момент его от-
крытия.
DEQ — объем расширения файла, принимаемый по умолчанию.
Число блоков, на которое расширяется файл, когда пространство
исчерпано. (Аргументы ALQ и DEQ имеют удобные для програм-
миста значения по умолчанию и часто опускаются.)
FAC — варианты доступа к файлу. В поле FAC необходимо заранее
определить те виды обработки, которые будут выполняться над
файлом. Это можно сделать операторами GET (ввести), PUT (вы-
вести), DEL (уничтожить) и UPD (модифицировать). При задании
нескольких операторов они заключаются в угловые скобки, напри-
мер:
FAC=<GET,UPD>
Поле FAC для каждого варианта содержит по одному биту. По
371
умолчанию, когда открывается существующий файл, бит GET в
FAB считается установленным, а при создании нового файла уста-
навливается бит PUT, поэтому для выполнения простого ввода-
вывода FAC можно опустить.
RFM — формат записи. Формат записи может быть фиксированным
FIX или переменным VAR. По умолчанию принимается VAR.
MRS —• максимальный размер записи. Для создаваемого файла
это поле заполняется программистом. Когда же открывается су-
ществующий файл, RMS загружает в него фактический максималь-
ный размер записи.
Важнейшим полем в FAB, которое не разрешается инициализи-
ровать программисту, является поле кода состояния завершения
(STS). В это поле RMS помещает код успешного или неудачного
завершения операции над файлом после попытки ее выполнения.
Например, после попытки открыть файл RMS может возвратить
программе код «файл не найден». Если сделана попытка обратиться
к файлу способом, который не определен в поле FAC, RMS возвра-
щает соответствующий код ошибки.
Пример 15.1. Блоки доступа к файлам для терминального ввода-вывода и к
файлу DATA.DAT. Блоки доступа к файлам терминального ввода и вывода, исполь-
зуемые макрокомандами READLINE, PRINTCHRS и DUMPLONG, а также FAB
для файла DATA.DAT, к которому обращается макрокоманда READRCRD, опре-
деляются следующим образом:
INFABt 8 FAB FNM-<SYS8INPUT>
OUTFАВI SFАВ FNM-<SYSBOUTPUT>,MRS-85
D18KFABI 8FAB FNM-<DATA.DAT >
Для инициализации других полей можно опираться на значения аргументов,
принятые по умолчанию, и данные, загружаемые RMS во время выполнения. Имена
файлов SYS$INPUT и SYS$OUTPUT являются логическими; они назначаются
терминалу пользователя по умолчанию. Однако их значения при выполнении про-
граммы можно изменить, поэтому^ например, вывод вместо терминала можно на-
править на диск. (Отметим, что в модуле IOMOD, приведенном в приложении D,
метки DISKFAB, INFAB и OUTFAB заканчиваются двумя двоеточиями, чтобы
объявить их глобальными именами и обеспечить обращение к ним из пользователь-
ской программы с помощью макрокоманд ввода-вывода.)
Блоки доступа к записям. Блок доступа к записи (RAB) содер-
жит 68 байтов данных, описывающих записи в файле. Каждый блок
доступа к записи ассоциируется с конкретным блоком доступа к
файлу. По макрокоманде $RAB резервируется память для RAB и
осуществляется некоторая инициализация. Часть полей заполняется
программой во время выполнения, а часть — RMS после завершения
операции. Как и $FAB, макрокоманда $RAB для большинства своих
аргументов имеет удобные значения по умолчанию. Формат макро-
команды $RAB:
метка: $RAB аргументы
Некоторые аргументы $RAB:
FAB — адрес блока доступа к файлу. Поле, содержащее адрес
FAB файла, в котором находятся записи, описываемые конкретным
372
RAB. Оно определяется записью метки, использованной в макро-
команде $FAB.
RAC — режим доступа к записи. Способ считывания или запо-
минания записей в файле. Варианты режима доступа SEQ (после-
довательный) и KEY (по ключу); по умолчанию принимается зна-
чение SEQ.
ROP — варианты обработки записи. Их может быть много.
Ниже в качестве примера- приведены три варианта применительно
к файлам терминального ввода:
РМТ — на терминале индицируется стимулирующее сообщение,
когда запрашивается входная строка;
CVT — все считываемые с терминала строчные буквы преобра-
зуются в прописные;
ТМО — вводится временное ограничение на ожидание ввода с
терминала.
При задании нескольких вариантов они заключаются в угловые
скобки, например:
ROP=<PMT, ТМО>
PBF — буфер стимула. Адрес стимулирующего сообщения, инди-
цируемого для терминального ввода. Пользователь должен помес-
тить стимулирующее сообщение в память.
PSZ — размер стимулирующего сообщения. Число байтов в сти-
мулирующем сообщении. Отметим, что при использовании PBF
и PSZ как вариант в поле ROP необходимо включить РМТ.
ТМО — тайм-аут. Максимальное время в секундах, отведенное
для завершения операции RMS (до 255 с). Отметим, что для исполь-
зования ТМО в поле ROP необходимо указать соответствующий
вариант.
UBF — пользовательский буфер. Адрес буфера для ввода, т. е.
адрес ячейки памяти, в которую RMS должна загрузить считанную
из файла запись. Это поле инициализируется в макрокоманде
$RAB, если используется стандартная ячейка, но программист
может заполнять это поле перед каждой операцией GET, указывая
конкретную ячейку для данной записи.
USZ — размер пользовательского буфера. Длина пользователь-
ского буфера.
RBF — буфер записи. Адрес буфера для вывода, т. е. адрес обла-
сти памяти, из которой RMS берет запись, передаваемую в файл.
Как и 'поле UBF, это поле инициализируется в макрокоманде
$RAB или заполняется программой во время выполнения.
RSZ — размер записи. Фактическое число байтов в считанной
записи при вводе. При выводе это длина записи, помещаемой в файл.
Как и FAB, блок доступа к записи имеет поле кода состояния
завершения (STS), в которое после выполнения каждой операции
RMS загружает код ее удачного или неудачного завершения.
Пример 15.2. Блоки доступа к записям для терминального ввода-вывода и
файла DATA.DAT. Модуль IOMOD содержит следующие блоки доступа к записям
373
и связанные с ними директивы:
LF - 10 Перевод строки
CR - 13 1 Возврат каретки
INRABi 8RAB FAB-INFAB,U8Z-80,- 1 Терминальный ввод
ROP-PMT,PBF-PROMPT,P8Z-S
OUTRABi BRAB FAB-OUTFAB 1 Терминальный вывод
DISKRABi 8RAB FAB-DISKFAB,USZ-80 1 Для DATA.DAT
PROMPTI .ASCII <LFXCR>/?7 / 1 Стимул
Поля буферов (UBF для ввода и RBF для вывода) заполняются макрокоман-
дами ввода-вывода READRCRD, READLINE и PRINTCHRS. Поле USZ инициа-
лизируется на 80, поэтому считается не более 80 байт. RAB для файла терминаль-
ного ввода определяет стимул длиной 5 символов, индицируемый при запрашива-
нии ввода с терминала.
Выше было показано, что некоторые поля в RAB должны уста-
навливаться программой во время выполнения. Для этого програм-
мист должен знать формат RAB. Чтобы освободить программиста
от необходимости изучения всех деталей формата (и упростить
модификацию последнего без переделок уже написанных программ),
для смещений полей предусмотрены определенные системой симво-
лические имена. В символических именах применяется стандартный
шаблон:
РАВ$т«л_«ля_поля
Например, RAB$L___UBF представляет собой смещение от начала
RAB поля (в длинное слово) адреса пользовательского буфера,
в RAB$B____PSZ — смещение байтового поля, в котором указан
размер стимулирующего сообщения. Некоторые из таких имен
применяются в описываемых ниже процедурах ввода-вывода в мо-
дуле IOMOD. Аналогичные определенные системой символические
имена предусмотрены и для смещений полей FAB.
Макрокоманды для обработки ввода-вывода. Эти макрокоманды
инициируют обработку файлов и записей во время выполнения.
Они используются в программе точно так же, как машинные коман-
ды (или макрокоманды ввода-вывода, рассмотренные в гл. 5), и
помещаются в тех ее местах, где должна инициироваться соответ-
ствующая операция. Для выполнения фактической операции обра-
батывающие макрокоманды вызывают системные процедуры.
Различают два вида таких макрокоманд — реализующие опера-
ции над файлами и над записями. Все макрокоманды обработки
имеют по три аргумента:
FAB или RAB — адрес FAB для обрабатываемого файла или
RAB для обрабатываемой записи;
ERR — точка входа подпрограммы обработки ошибок;
SUC — точка входа подпрограммы, которая выполняется при
успешном завершении операции.
Аргументы ERR и SUC не обязательны; если они опущены, сле-
дующей выполняется команда, которая находится в программе
после макрокоманды.
374
Макрокоманды обработки файла. Наиболее часто для операций
над файлами применяются макрокоманды:
$OPEN — сделать существующий файл доступным для обработки;
$CREATE — создать новый файл и открыть его;
SCLOSE — прекратить доступ к файлу.
До начала обработки файл должен быть открыт, а по ее завер-
шении — закрыт.
В RMS имеются и другие макрокоманды:
$ERASE — удалить файл и стереть его элемент каталога (до
стирания файл должен быть закрыт);
$EXTEND — увеличить объем памяти, распределяемой файлу
(до расширения файл должен быть открыт).
Макрокоманды обработки записей. Макрокоманды обработки
записей реализуют операции над записями или блоками доступа к
записям. Основными операциями ввода и вывода являются ввести
(get) и вывести (put). Некоторого пояснения требуют операции
подключить (connect) и отключить (disconnect). До выполнений
операций записи и считывания блок доступа к записи необходимо
явно «подключить» к блоку доступа к файлу. Напомним, что каждый
RAB имеет поле (поле его FAB), которое содержит адрес ассоции-
руемого с ним FAB. Возможно, что несколько RAB определяют
один и тот же FAB, поэтому конкретный RAB выбирается во время
выполнения. (Для последовательных файлов к конкретному FAB
одновременно может быть подключен только один RAB.)
Приведем некоторые макрокоманды обработки записей:
$CONNECT — подключить указанный RAB к соответствующему
FAB, распределить буферы для ввода-вывода, установить указатель
на первую запись в файле:
SDISCONNECT — отключить RAB от соответствующего FAB;
$GET — ввести запись. RMS помещает запись в область, опре-
деленную полем UBF, и загружает значение ее длины в поле RSZ
блока доступа к записи. Макрокоманда устанавливает поле STS,
показывая любые ошибки или необычные условия (например, конец
файла).
$PUT — вывести запись.
Пример 15.3. Макрокоманды BEGIN и EXIT. Макрокоманды BEGIN и EXIT в
модуле IOMAC инициируют инициализацию и завершение программы при исполь-
зовании RMS. Определение макрокоманд имеет вид:
.MACRO BEGIN NAME
.ENTRY •OPEN NAME,Z*M<IV,DV> । Определить точку входа I Установить прерывание по переполнению FAB«INFAB । Открыть файл терминального ввода
•CONNECT •CREATE RAB-INRAB FAB-OUTFAB $ Соодать файл терминального вывода
•CONNECT •OPEN RAB-OUTRAB FАВ"DISKFAB ; Открыть файл дискового ввода
375
«CONNECT RAB-DISKRAB
.ENDM BEGIN
.MACRO EXIT
«CLOSE FAB"INFAB 1 Закрыть файлы ввода-вывода
«CLOSE FAB"OUTFAB •i
«CLOSE FAB"DI8KFAB 1 II
.ENDM «ЕХ1Т.8 EXIT 1 Системная макрокоманда выхода
Пример 15.4. Макрокоманда READLINE. Простейшей операцией ввода-вывода
в модулях IOMAC и IOMOD является операция, реализуемая READLINE. Макро-
команды вида READRCRD и PRINTCHRS вызывают процедуру, которая и осуще-
ствляет все необходимые действия. (При этом макрокоманды становятся очень
короткими и их можно использовать в циклах с байтовыми смещениями для назна-
чений переходов.) Определение макрокоманды READLINE:
.MACRO READLINE WHERETO
PUSHAB WHERETO । Адрес пользовательского буфера
CALLS #1,RDLINE
READLINE
Процедура RDLINl/:
I ПРОЦЕДУРА RDLINE (WHERETO)
I
I Эта процедура воспринимает входную строку с терминала. Она использует
I RAB, отмеченный INRAB, который вызывает индикацию на терминале стимула.
I Входная запись помещается в памяти по адресу WHERETO, который является
I аргументом RDLINE. Процедура возвращает длину записи в регистре R0.
I
.ENTRY RDLINE,О
MOVL 4(АР),INRAB+RAB«L_UBF J Заполнить поле UBF в RAB
«GET RAB"INRAB * | Воспринять запись
CVTWL INRAB+RAB«W_RBZ,R0 | Длина записи в RO
RET
Пример 15.5. Макрокоманда READRCRD. Ее определение:
.MACRO READRCRD WHERETO,?LBL
PUSHAB WHERETO | Адрес пользовательского буфера
CALLS «1,RDRCRD j Возвращает длину в RO
В NEO LBL | Ввести запись
BRW EOF ; Конец файла
LBLi .ENDM READRCRD
Процедура R DR CRD:
; ПРОЦЕДУРА RDRCRD
; Эта процедура считывает следующую запись из файла
376
; DATA . DAT. Если запись в файле есть, она запоминается
; по адресу WHERETO, а длина ее загружается в
; регистр R0. Когда же записей .больше нет, то бит Z
; в запомненном PSW устанавливается, показывая
; конец файла.
,* Процедура RDRCRD использует RAB, отмеченный DISKRAB.
.ENTRY RDRCRD
MOVL 4 (АР), DISKRAB + RAB$L _ UBF
; Заполнить поле UBF в RAB
$GET RAB = DISKRAB ; Ввести запись
CMPL DISKRAB + RAB$ _ STS, #RMS$ _ EOF
; Проверить поле STS на EOF
BEQL EOF
CVTWL DISKRAB + RAB$W _ RSZ, RO ; Размер записи в RO
RET
EOF: BISB2 #AX04,4(FP) ; Установить флаг EOF
RET • '
Здесь RMS$_____EOF — имя, определяемое системой, значением которого явля-
ется код состояния конца файла.
Читателю рекомендуется рассмотреть макрокоманду PRINTCNRS и процедуру
PTCHRS, которые приведены в приложении D.
15.3. Упражнения
Ни в одном из упражнений не используются макрокоманды из модуля IOMAC.
1. Предположим, что для ввода с терминала FAB, RAB и другие блоки опре-
делены следующим образом:
LF = 10 ; Перевод строки
CR = 13 1 Возврат каретки
INFАВ: •FAB FNM-<8Y8BINPUT>
INRAB: •RAB FAB-INFAB,UBF-INBUF,U8Z-80,-
ROP-PMT,PBF-M8S,P8Z-8
INBUF: . BLKB 80
MSB: .ASCII <LFXCR>/INPUT: /
LINE: .BLKB 80
а) Считая, что файл уже открыт, напишите команду (или команды) для считы-
вания строки с терминала и запоминания ее в LINE.
б) Какое сообщение-стимул, извещающее пользователя о том, что ожидается
ввод, появится на экране?
2. Предположим, что для вывода на терминал FAB, RAB и другие блоки
определены следующим образом:
LF = 10 ; Перевод строки
CR = 13 1 Возврат каретки
SPACE - 32 OUTFABi BFAB OUTRAB: SRAB OUTBUF: .ASCII .BYTE ; Пробел FNM-< 8Y880UTPUT >,MRS-85 FAB-OUTFAB,RBF-OUTBUF,R8Z-85 <LFXR> SPACEC853
377
а) Считая, что файл уже открыт, напишите команду (или команды) для печати
10-символьного сообщения, хранимого в памяти по адресу. MESSAGE.
б) Напишите команду (или команды) для печати еще одного сообщения, нахо-
дящегося в TITLE и содержащего восемь символов.
3. Напишите программу для решения следующей проблемы. На диске имеются
два файла DATA1.DAT и DATA2.DAT, которые содержат 80-байтовые записи,
начинающиеся с восьмисимвольного ключа. Записи в файлах рассортированы в
алфавитном порядке по ключам. Программа должна построить новый единый файл
RECS.DAT, все записи которого также размещены по порядку. Иными словами,
программа должна слить два файла. После слияния оба исходных файла необходи-
мо удалить. (Программа должна быть модульной, т. е. по возможности использо-
вать процедуры.)
Система команд
(в алфавитном порядке)
Приложение А
379
Мнемоника Операнды Операция Код операции
1 2 3 4
АСВх ADDx2 ADDx3 . ADDP42 ADDP63 ADWC AOBLEQ limit. x,incr.x,index.xfdest.w x = B, W, L, F, D, G, H opljc,op2.x x = B, W, L, F, D, G, H opl.x,op2.x,sum.x x = B, W, L, F, D, G, H dgtsl.w,pkdl .b,dgts2.w,pkd2.b dgtsl.w,pkdl.b,dgts2,w,pkd2.b, dgts3.w,pkd3.b opl.l,op2.1 limi t. l,ind ex. l,dest. b Прибавить, сравнить и перейти Сложить Сложить Сложить упакованные Сложить упакованные Сложить с переносом Прибавить 1; перейти, если ^0 АСВВ 9D ACBW 3D ACBL F1 ACBF 4F ACBD 6F ACBG 4FFD АСВН 6FFD ADDB2 80 ADDW2 АО ADDL2 СО ADDF2 40 ADDD2 60 ADDG2 40FD ADDH2 60FD ADDB3 81 ADDW3 А1 ADDL3 С1 ADDF3 41 ADDD3 61 ADDG3 41FD ADDH3 61FD 20 21 D8 F3
AOBLSS limit. l,index.l,dest.b Прибавить 1; перейти, если <0 F2
ASHx count.b,src.x,dest.x x = L,Q f Сдвинуть арифметически ASHL 78 ASHQ 79
Продолжение приложения А
1 2 3 4
ASHP2 count.b,srcdgts.w,src.b,round.b, dstdgts,w,dest.b Сдвинуть арифметически и округлить упакованное F8
ВВС posn.l,base.b,dest.b Перейти, если бит сброшен Е1
ввсс posn. 1,base, b,dest.b Перейти, если бит сброшен, и сбросить его Е5
BBCS posn.l.base.b,dest.b Перейти, если бит сброшен, и устано- вить его ЕЗ
BBS posn.l.base.b .dest.b Перейти, если бит установлен ЕО
BBSC posn.ljbase.b,dest.b Перейти, если бит установлен, и сбро- сить его Е4
BBSS posn.l .base.b,dest.b Перейти, если бит установлен, и уста- новить его Е2
вес dest.b Перейти, если перенос сброшен 1Е
BCS dest.b Перейти, если перенос установлен 1F
BEQL dest.b Перейти, если равны 13
BEQLU dest.b Пёрейти, если равны (беззнаковые) 13
BGEQ dest.b Перейти, если больше или равны 18
BGEQU dest.b Перейти, если больше или равны (беззнаковые) 1Е
BGTR dest.b Перейти, если больше 14
BGTRU dest.b Перейти, если больше (беззнаковые) 1А
BICx2 mask.x,dest.x x=B, W, L Сбросить биты BICB2 8А BICW2 АА B1CL2 СА
BICx3 mask.x,src,x,dest.x x = B,W, L Сбросить биты BICB3 8В BICW3 АВ B1CL3 СВ
BICPSW, mask.w Сбросить биты в PSW В9
BISx2 mask.x.dest.x x = B, W, L Установить биты - B1SB2 88 BISW2 А8 B1SL2 С8
BISx3 m ask. x, src.x,dest.x x = B, W, L Установить биты BISB3 89 BISW3 А9 BISL3 С9
BISPSW ВПх mask.w mask.x,src.x x = B, W, L
BLBC src.l,dest.b
BLBS BLEQ BLEQU src. 1,dest.b dest.b dest.b
BLSS BLSSU BNEQ BNEQU BRB BRW BSBB dest.b dest.b dest.b dest.b dest.b dest.fr dest.b
BSBW dest.w
BVC BVS dest.b dest.b
CALLG arglst.l,proc.b
CALLS numargs.l.proc.b
CASEx selector. x,base.x,limit.x,dstUst.w x=B, W, L
CLRx dest.x x=B, W, L, Q, F, D, G, H
СМРх
opljc,op2.x
х=В, W, L, F, D, G, Н
Установить биты в PSW В8
Проверить биты . BITB 93
BITW ВЗ
BITL D3
Перейти, если младший бит сброшен Е9
Перейти, если младший бит установлен Е8
Перейти, если меньше или равны 15
Перейти, если меньше или равны 1В
(беззнаковые)
Перейти, если меньше 19
Перейти, если меньше (беззнаковые) 1F
Перейти, если не равны 12
Перейти, если не равны (беззнаковые) 12
Перейти (смещение — байт) 11
Перейти (смещение — слово) 31
Перейти к подпрограмме (смещение — 10
байт)
Перейти к подпрограмме (смещение — 30
слово)
Перейти, если переполнение сброшено . 1С
Перейти, если переполнение установ- 1D
лено
Вызвать процедуру (общий список FA
аргументов)
Вызвать процедуру (список аргумен- FB
тов в стеке)
Выбрать CASEB 8F
CASEW AF
CASEL CF
Сбросить CLRB 94
CLRW В4
CLRL=CLRF D4
CLRQ = CLRD = CLRG 7С
CLRO = CLRH 7CFD
Сравнить СМРВ 91
CMPW Bl
CMPL DI
Продолжение приложения А
1 2 3 4
CMPF CMPD 51 71
СМРСЗ2 len. w,strl.b,str2.b Сравнить символы CMPG СМРН 51FD 71FD 29
СМРС52 lenlAU,strl.b,fill.b,len2.w,str2.b Сравнить символы 2D
СМРРЗ2 dgts.w,pkdl.b,pkd2.b Сравнить упакованные 35
СМРР42 dgtsl .w,pkdl .b,dgts2w,pkd2.b Сравнить упакованные 37
CMPV posn. 1 ,size.b,base.b,long. 1 Сравнить двоичное поле ЕС
CMPZV posn. 1 ,size. b,base.b,long. 1 Сравнить двоичное поле, расширенное ED
CVTxi/ CVTBW 99 CVTBL 98 CVTBF 4С CVTBD 6С CVTBG4CFD CVTBH6CFD CVTFB 48 CVTFW49 CVTFL 4А src.x,dest.y xy может быть любой из 40 комби- наций в приведенных ниже коман- дах CVTWB 33 CVTLB F6 CVTWL 32 CVTLW F7 CVTWF 4D CVTLF 4Е CVTWD 6D CVTLD 6E CVTWG 4DFD CVTLG 4EFD CVTWH 6DFD CVTLH 6EFD CVTDB 68 CVTGB 48FD CVTDW 69 CVTGW49FD CVTDL 6A CVTGL 4AFD нулем Преобразовать CVTHB 68FD CVTHW 69FD CVTHL 6AFD
CVTFD 56 CVTFG99FD CVTFH98FD CVTLP2 CVTDF 76 CVTGF 33FD CVTDH32FD CVTGH 56FD src.l,dgts.w,pkd.b CVTHF F6FD CVTHD F7FD CVTHG 76FD Преобразовать длинное в упакованное F9
CVTPL2 dgts.w,pkd.b,dest.l Преобразовать упакованное в длинное 36
CVTPS2 dgtsl.w,pkd.b,dgts2.w,lsn.b Преобразовать упакованное в сим- 08
CVTPT2 dgtsl .w,pkd.b,tb I. b,dgts2. w,trl.b вольный код Преобразовать упакованное в сим- 24
CVTRxL src.x,dest.l вольный код (знак — последний) Преобразовать округленное в длинное CVTRFL 4В
x=F, D, G, H
383
CVTSP2 dgtsl .w,lsn.b,dgts2.w,pkd.b
CVTTP2 dgtsl .w,trl.b,tbl.b,dgts2.w,pkd.b
DECx op.x x=B, W, L
DIVx2 dvsr.x,op2.x x = B, W, L, F, D, G, H
DIVx3 dvsrjc,dvdd.x,quo.x x = B, W, L, F, D, G, H
DIVP34 dgtsl .w,d vsr. b,dgts2,w,dvdd. b,
EDITPC3 EDIV EMODx EMODx dgts3.w ,quo.b dgts. w,pkd. b,patrn. b,dest. b dvsr.l,dvdd.q,quo.l,rem.l mUlr.xjnulrex.b,muldjc,int.l,frac.x x=F, D mulr.x,mulrex.w,muld.xfint.l,frac. x = G, H
EMUL EXTV EXTZV mul r.l, muld.l,add. l,prod.q posn.l, size.b,base.b,dest. 1 posn.l, size.b, base. b,dest.l
FFC posn.l,size.b,base.b,dest.1
Преобразовать символьный код в упа-
кованное
Преобразовать символьный код
(знак — последний) в упакованное
Декремент
Разделить
Разделить
Разделить упакованные
Отредактировать упакованное
Расширенное деление
Разделить с повышенной точностью
Расширенное умножение
Выделить двоичное поле
Выделить двоичное поле с нулевым
расширением
Найти первый сброшенный бит
CVTRDL 6В
CVTRGL 4BFD
CVTRHL 6BFD
09
26
DECB 97
DECW В7
DECL D7
DIVB2 86
DIVW2 Аб
DIVL2 С6
DIVF2 46
DIVD2 66
DIVG2 46FD
DIVH2 66FD
DIVB3 87
DIVW3 А7
DIVL3 С7
DIVF3 47
DIVD3 67
DIVG3 47FD
DIVH3 67FD
27
38
7В
EMODF 54
EMODD 74
EMODG 54FD
EMODH 74FD
7А
ЕЕ
EF
ЕВ
1 2
FFS posn.l,size. b,base.b,dest. 1
INCx op.x x=B, W, L
INDEX subsc.1,low.1,high.1,sized,base.1, offset.l
;1NSQUE entry.b,addr.l
1NSV src. l,posn.'l,size. b,base.b
JMP dest.b
JSB dest.b
LOCC* char.b,ten. w,str.b
МАТСНС2 lent. w,strl.b,len2.w,str2,b
MCOMx । src.x,dest.x x = B, W, L
MNEGx spc.x,dest,x x=B, W, L, F, D, G, H
MOV Ax
MOVx
srcjc,dest.l
x = B, W, L, Q, O, F, D, G, H
src.x,dest.x
x = B, W, L, Q, O, F, D, G, H
Продолжение приложения А
3 Г 4
Найти первый установленный бит EA
Инкремент INCB 96
INCW B6
INCL D6
Вычислить индекс массива OA
Ввести в очередь OE
Ввести двоичное поле FO
Перейти 17
Перейти к подпрограмме 16 i
Локализовать символ ЗА 1
Соответствие символов 39
Переслать дополненное МСОМВ 92
MCOMW B2
< MCOML D2
Переслать с изменением знака MNEGB 8E
MNEGW AE
MNEGL CE
MNEGF 52
MNEGD 72
MNEGG 52FD
MNEGH 72FD
Переслать адрес MOVAB 9E
MOVAW 3E
MOVAL = MOVAF DE
MOVAQ = MOVAD = MOVAG 7E
* MOVAO = MOVAH 7EFD
Переслать MOVB 90
MOVW BO
MOVL DO
• MOVQ 7D
MOVO 7DFD
MOVF 50
Зак. 821
MOVC33 MOVC53 MOVP2 MOVPSL MOVTC len.w.strl.b,str2.b lenl.w ,strl .b,fil I. b, len2. w,s tr2. b dgts. w,pkdl.b,pkd2.b dest.l srclen.w,src.b,fill.b,tbl.b,dstlen.w, dest.b
MOVTUC srclen.w,src.b, escape.b,tbl.b,dstlen. w,dest.b
MOVZxy I src.x,dest.x x{/=BW, BL, WL
MULx2 opl.x,op2.x x=B, W, L, F, D, G, H
MULx3
mul r.x,muld.x,prod.x
x = B, W, L, F, D, G, H
MULP3 mulrlen.w,mulr.b,muldlen.w,muld. b,prodlen.w,prod.b
NOP POLYF® arg.f degree. w,coefs.b
POLYx3 arg jc, degree. w,coefs. b x = D, G, H
POPR mask.w
w PUSHAx op.x x = B?W, L, Q, 0, F, D, G, H
MOVD
MOVG
MOVH
70
50FD
70FD
Переслать символы 28
Переслать символы 2С
Переслать упакованное 34
Переслать PSL DC
Переслать преобразованные символы 2Е
Пересылать преобразованные символы 2F
до переключения •
Переслать с нулевым расширением MOVZBW 9В
MOVZBL 9А
' MOVZWL ЗС
Умножить MULB2 84
MULW2 А4
MULL2 С4
MULF2 44
MULD2 64
MULG2 44FD
MULH2 64FD
Умножить MULB3 85
MULW3 А5
MULL3 С5
MULF3 45
MULD3 65
MULG3 45FD
MULH3 65FD
Умножить упакованные 25
Холостая команда 01 .
Вычислить полином 55
Вычислить полином POLYD 75
POLYG 55FD
POLYH 75FD
Извлечь из стека в регистры ВА
Включить в стек адрес PUSHAB 9F
PUSHAW 3F
1 2
PUSHL long A
PUSHR mask.w
REMQUE RET entry,b,addr.l
ROTL count.b,src.l,dest.l
RSB SBWC op 1.1,op 2.1
SCANC2 len.w,str. b,tbl.b,mask. b
SKPC1 char.b,len.w,str.b
SOBGEQ index .1, dest.b
SOBGTR index, l.dest.b
SPANC len.w,str.b,tbl.b,mask.b
SUBx2 op 1.x,op 2.x x = B, W, L, F, D, G, H
SUBx3 opl .x,op2.x,dif.x x = B, W, L, F, D, G, H
Продолжение приложения А
4
Включить в стек длинное слово
Включить в стек регистры
Удалить из очереди
Возврат из процедуры
Циклически сдвинуть
Возврат из подпрограммы
Вычесть с заемом (переносом)
Сканировать с поиском символа
Пропустить символ
Вычесть 1; перейти, если ^0
Вычесть 1; перейти, если >0
Пропускать символы
Вычесть
Вычесть
PUSHAL = PUSHAF DF
PUSHAQ=PUSHAD= 7F
=PUSHAG
PUSHAO = PUSHAH 7FFD
DD
BB
OF
04
9C
05
D9
2A
3B
F4
F5
2B
SUBB2 82
SUBW2 A2
SUBL2 C2
SUBF2 42
SUBD2 62
SUBG2 42FD
SUBH2 62FD
SUBB3 83
SUBW3 A3
SUBL3 C3
SUBF3 43
SUBD3 63
SUBG3 43FD.
SUBH3 63FD
оо « SUBP42 SUBP63 len. 1 .w,pkd l.b,len2. w,pkd2. b lenl.w,pkdl .b,len2,w,pkd2.b,len3.w, pkd3.b Вычесть упакованные Вычесть упакованные 22 23
TSTx op.x Проверить TSTB 95
x = B, W, L, F, D, G, H TSTW В5
TSTL D5
TSTF 53
TSTD 73
TSTG 53FD
TSTH 75FD
XFC Определяемая пользователем Расширенная функция FC
X0Rx2 mask.x,dest.x Сложить по модулю 2 X0RB2 8С
x=B, W, L XORW2 АС
X0RL2 СС
X0Rx3 mask.x,src.x,dest.x Сложить по модулю 2 X0RB3 8D
x=B, W, L XORW3 AD
4 X0RL3 CD
Z8F
Следующие команды используются только или главным образом
операционной системой
ADAWI opl.w,op2,w Прибавить выравненное слово (блокированная) 58
BBCCI BBSSI ВРТ CHMz po sn.l, base, b, dest.b posn.l,base.b,dest.b parameter, w Перейти, если бит сброшен, и сбросить его (блокированная) Перейти, если бит установлен, и уста- новить его (блокированная) Ошибка контрольной точки Изменить режим на z СИМЕ Е7 Е6 BD
CRC HALT5 INSQHI z = E (исполнительная программа), К (ядро), S (супервизор), U (поль- зователь) tbl.b, initial./, len.w, stream. b,dest. 1 entry.bjieader.q Вычислить символ избыточного цик- лического контроля Остановить Ввести в голову очереди (блокиро- ванная) снмк CHMS CHMU ВС BE BF ОВ 00 5С
Продолжение приложения А
1 2 3 4
INSQTI LDPCTX MFPR5 MTPR5 PROBER PROBEW REI REMQHI REMQTI SVPCTX5 1 Использу< стеке. * Выполни Примечай» entry.bjieader.q procreg.1,dest.l src.l,procreg.l mode, b, ten. w,base.b mode, b,ten. w.base. b header. q,addr.l header. q,addr.l st регистры RO—Rl. 2 Использует регистры нется только ядром операционной системы. е. В таблице операнды показаны в формате и Ввести в хвост очереди (блокирован- ная) Загрузить контекст процесса Переслать из регистра процесса Переслать в. регистр процесса Возврат из особого случая или пре- рывания Удалить элемент из головы очереди Удалить элемент из хвоста очереди Запомнить контекст процесса RO— R3. ’Использует регистры RO—R5. 4Испол ияя.тип. (тип операндов-цепочек обозначен через 5D 06 DB _DA_ ОС 0D 02 5Е 5F 07 ъзует рабочее пространство в пользовательском Ь)
Приложение В
Таблицы преобразования шестнадцатеричных чисел и степеней 2
Таблица преобразования шестнадцатеричных чисел
Х^азряд ЦифрХ. Позиционное значение шестнадцатеричной цифры
I67 , 16е 16s I64 I63 16s 161 16°
0 0 0 0 0 0 0 0 0
1 268435456 16777216 1048576 65536 4096 256 16 1
2 536870912 33554432 2097152 131072 8192 512 32 2
3 805306368 50331648 3145.728 196608 12288 768 48 3
4 1073741824 67108864 4194304 262144 16384 1024 64 4
5 1342177280 83886080 5242880 327680 20480 1280 80 5
6 1610612736 100663296 6291456 393216 24576 1536 96 6
7 1879048192 117440512 7340032 458752 28672 - 1792 112 7
8 2147483648 134217728 8388608 524288 32768 2048 128 8
9 2415919104 150994944 9437184 589824 36864 2304 144 9
А 2684354560 167772160 10485760 655360 40960 2560 160 А
В 2952790016 184549376 11534336 720896 45056 2816 176 В
С 3221225472 201326592 12582912 786432 49152 3072 192 С
D 3489660928 218103808 13631488 851968 53248 3328 208 D
Е 3758096384 234881024 14680064 917504 57344 3584 224 Е
F 4026531840 251658240 15728640 983040 61440 3840 240 F
Таблица степеней 2
р 2р р 2Р Р 2Р Р 2Р
0 1 8 256 16 65536 24 16777216
1 2 9 512 17 131072 25 33554432
2 4 10 1024 18 262144 26 67108864
3 8 11 2048 19 524288 27 134217728
4 16 12 4096 20 1048576 28 268435456
5 32 13 8192 21 2097152 29 536870912
6 64 14 16384 22 4194304 30 1073741824
7 128 15 32768 23 8388608 31 2147483648
32 4294967296
389
Приложение С
Символьный код ASCII
16*ричное число Десятичное число Код ASCII 16-ричное число Десятичное число Код ASCII
00 0 NUL 20 32 SP
01 1 SOH 21 33 1
02 2 STX 22 34 »
03 3 ETX 23 35 *
04 4 EOT 24 36 $
05 5 ENQ 25 37 %
06 6 ACK 26 38 &
07 7 BEL 27 39
08 8 BS 28 40 (
09 9 HT 29 41 )
0А 10 LF 2A 42 *
ОВ 11 VT 2B 43 +
ос 12 FF 2C 44
0D 13 CR 2D 45 —
ОЕ 14 SO 2E 46
OF 15 SI 2F 47 /
10 16 DLE 30 48 0
11 17 DC1 31 49 1
12 18 DC2 32 50 0
13 19 DC3 33 51 3
14 20 DC4 34 52 4
15 21 NAC 35 55 5
16 22 SYN 36 54 6
17 23 ETB 37 55 7
18 24 CAN 38 56 8
19 25 EM 39 57 9
1А 26 SUB ЗА 58
1В 27 ESC ЗВ 59 < »
1С 28 FS ЗС 60
ID 29 GS 3D 61 =
IE 30 RS ЗЕ 62
IF 31 US 3F 63 ?
40 64 @ 60 96 \
41 65 A 61 97 а
42 66 В 62 98 в
43 67 C 63 99 с
44 68 D 64 100 d
45 69 E 65 101 е
46 70 F 66 102 f
47 71 G 67 103 g
48 72 H 68 104 h
49 73 I 69 105 i
4A 74 J 6А 106
4B 75 К 6В 107
4C 76 L 6С 108
4D 77 M 6D 109 m
4E 78 N 6Е ПО n
4F 79 0 6F 111 0
> 50 80 P 70 112 P
51 81 Q 71 113 q
52 82 R 72 114 г
53 83 S 73 115 s
54 84 T 74 116 t
390
55 85 и 75 117 и
56 86 V 76 118 V
57 87 W 77 119 W
58 88 X 78 120 X '
59 89 Y 79 121 у
5А 90 Z 7А 122 2
5В 91 f 7В 123
5С 92 \ 7С 124
5D 93 ] 7D 125
5Е 94 А 7Е 126
5F 95 7F 127 DEL
Приложение D
Макроопределения и процедуры ввода-вывода
Файл IOMAC
Для создания макробиблиотеки ЮМАС приведенные ниже макроопределения
необходимо поместить в файл, называемый IOMAC.MAR. Команда
LIBRARY/CREATE/MACRO IOMAC.MLB IOMAC.MAR
создает библиотечный файл IOMAC.LIB.
•MACRO BEGIN •ENTRY NAME,*M<IV,DV> 1 Определить точку входа
1 Установить прорывание по
1 переполнении
•OPEN FAB-INFAB 1 Открыть Файл терминального
1 овода
•CONNECT RAB-INRAB
•CREATE FAB-OUTFAB 1 Создать файл терминального
1 вывода
•CONNECT RAB-OUTRAB
•OPEN FAB-DISKFAB 1 Открыть Файл диенового ввода
•CONNECT RAB-DI8KRAB
• ENDM BEGIN
.MACRO READLINE WHERETO
PUSHAB WHERETO 1 Адрес польеоФательского буфера
CALLS •l.RDLINE
.ENDM READLINE
.MACRO READRCRD WHERETO,7LBL
PUSHAB WHERETO 1 Адрес пользовательского*
1 •уфора
CALLS •l.RDRCRD 1 Возвращает длину о RO
BNEQ LBL 1 Ввести запись
BRW EOF 1 Конец файла
LBLl .ENDM READRCRD
1
.MACRO PRINTCHRS STRING,LENGTH-#85
CVTWL LENGTH,-(BP) 1 Сохранить длину цепочки
PUSHAB STRING 1 Сохранить адрес цепочки
CALLS •2,PTCHRS
391
.ENDM PRINTCHRS
I '
.MACRO DUMPLONG ARGl,ARG2,ARG3,ARG4,ARe5,ARG6,-
ARG7,AR68,ARG9,ARG1O,ARG11,ARG12
CALLS AO,STARX | Напечатать звездочки
.IRP ARG,<ARG1,ARG2,ARG3,ARG4,ARG5,ARG6,-
ARG7,AR68,ARS9,ARG1O,ARG11,ARG12>
-IF NOT_ВLANK ARG
MOVQ •*A/XEXTRACT(0,8,ARG)/,-(SP) J Сохранить имя
PUSHL SP 1 Адрес имени
PUSHL ARG 1 Длинное слово для печати
CALLS •2.CVTPRT
ADDL2 AB, BP 1 Извлечь имя
.ENDC
.ENDR
CALLS AO.STARX j Напечатать звездочки
.ENDM DUMPLONG
.MACRO EXIT
•CLOSE FAB-INFAB 1 Закрыть файлы ввода-вывода
•CLOSE FAB-OUTFAB 1
•CLOSE FAB—DI8KFAB 1
•EXIT.S 1 Системная макрокоманда выхода
.ENDM EXIT
Модуль IOMOD
Модуль IOMOD.OBJ содержит блоки FAB и RAB, используемые макрокомандами
ввода-вывода, и процедуры, которые они вызывают для выполнения операций вво-
да-вывода. Для построения файла необходимо напечатать приведенный ниже файл
IOMOD.MAR, а затем осуществить ассемблирование по команде:
MACRO IOMOD*IOMAC/LIB
.PSECT 10.DATA,LONG,NOEXE
1 LF - 10 1 Перевод строки
CR - 13 1 Возврат каретки
8РС - 32 I Пробел
INFABii «FAB FNM—<8Y8AINPUT> 1 Терминальный ввод
OUTFABl1 AFAB FNM—<8Y8A0UTPUT>,MR8—85 । Терминальный вывод
DIBKFABlI AFAB FNM-<DATA.DAT> I Для Файла DATA.DAT
INRABII ARAB FAB-INFAB,U8Z-80,-
ROP-PMT, PBF—PROMPT, PBZ-5
OUTRABli GRAB FAB-OUTFAB
DISKRAB11 ARAB FAB-DI8KFAB,U8Z-80
PROMPT! .ASCII <LFXCR>/?7 /
•
.PSECT I0.PR0C8,NOWRT
I
I ПРОЦЕДУРА RDLINE (WHERETO)
I
I Эта процедура воспринимает входную строку с терминала.Она использует
I RAB, отмоченный INRAB, который вызывает индикацию на терминале стимула.
I Входная оапись помещается о памяти по адресу WHERETO, который является
I Аргументом RDLINE. Процедура возвращает длину записи в регистре RO.
392
.ENTRY RDLINE,О
MOVL 4 CAP),INRABTRABBL.UBF J Заполнить пола UBF RAI
•GET RAB!NRAB । Воспринять вались
CVTWL INRAB+RABBW.RSZ,R0 | Длина записи a RO
RET
I ПРОЦЕДУРА RDRCRD (WHERETO)
I
I Эта процедура считывает следующую вались ио файла DATA.DAT. Если вались
I в файле есть, она запоминается по адресу WHERETO, а длина помещается в
I регистр RO. Когда же записей Вольве нот, Фит Z в запомненном P8W уста**
I навливавтся, показывая конец файла. Процедура RDRCRD иСпользует__ЯА>^^
I отмоченный DI8KRAB. *
I
.ENTRY RDCRD,0
MOVL 4(АР),DISKRAB*RAB®L.UBF | Запомнить полФ UBF в RAB
•GET RAB-DIBKRAB | Ввести запись
CMPL DIBKRAB+RABBL.STS,BRMSB_EOF | Проворить поло GTB на EOF
BEQL EOF
CVTWL DIGKRA3+RAB*N.RSZ,RO | Размер записи в RO
RET
EOFI BI6B2 t*X04,4(AP) | Установить Флаг EOF
RET
ПРОЦЕДУРА PTCHRG (STRING, MAX.LEN)
Эта процедура индицирует на терминале символьную цепочку. Пород печатью
цепочки осуществляется возврат' каретки и перевод .строки.
Входные аргументы
STRING индицируемая цепочка
MAX.LEN максимальная длина цепочки (передается непосредственным
значением)
Индицируются максимум MAX.LEN символов, но Файт из нулей
интерпретируется как тФрминатор цепочки.
CR.LFi .BYTE 13,10 j Возврат каретки и перевод строки
1 .ENTRY PTCHRG,0
MOV AN CR.LF,OUTRAB+RABM..RBF 1 Установить поля RBF и RSZ
MOVW •2,OUTRAB+RABBW.RGZ 1 на CR и LF
•PUT RAB-OUTRAB f Вывести CR и LF
MOVL 4(AP),OUTRAB+RABBL.RBF 1 Адрес STRING в поле RBF
LOCC •0,8(AP),«4(AP) 1 Искать нулевой Файт
SUBL2 4(AP),R1 1 Длина цепочки
MOVW Rl,OUTRAB+RAB«W.RSZ | Передать длину в поле RSZ
•PUT RAB-OUTRAB 1 Вывести цепочку
RET
| ПРОЦЕДУРА STARX
I Эта процедура печатает по три звездочки в начале и конце выхода DUMPLONG.
STARS» .ASCIZ /«««/
393
.ENTRY STARX,0
PRINTCHRS STARS,#3
RET
I
I
| ПРОЦЕДУРА CVTPRT (LONG,NAME)
I
I Эта процедура преобразует длинно* слово в шестнадцатеричную символьную
I цепочку и печатает вместо с ее именем. Список аргументов содержит длинное
I слово, передаваемое непосредственным значением, и адрес символьной цепоч-
Г*ки (максимальнее длина 8 символов), т.е. имя длинного слова.
I Для преобразования CVTPRT использует процедуру 0TS8CVT.L.TZ из библиотеки
I времени выполнения.
.PSECT ID.DATА
LONG! .BLKL 1
DUMPI .BLKB 18
I
.PSECT 10.PROCS
DESCi .LONS ЛХ010Е000В
.ADDRESS DUMP*10
ARBSi .LONS 3
.ADDRESS LONS,DESC
.LONG 8
I
ENTRY CVTPRT,AM<R2,R3,R4,RS>
I
MOVCS 80,0,8SPC,810,DUMP
LOCC 80,88,88(AP)
8UBL3 R0,88,R2
M0VC3 R2,88(AP),DUMP
MOVL 4(AP),L0NG
CALLG ARGS,G*OTS1CVT.L.TZ
PRINTCHRS DUMP,818
RET
I
END
I Для вывода DUMPLONG
I Библиотечная процедура требует
I дескриптора цепочки
I Список аргументов
I Пробелы в буфер DUMP
I Искать конец имени цепочки
I Длина имени цепочки
I Передать имя в буфер DUMP
I Адрес длинного слова в список
I аргументов
I Подпрограмма преобразования
I Вывод
Приложение Е
Ответы на некоторые упражнения
Глава 3
L a) 10000001000111 B) 111110101101
2. a) 1A37CB B) 100
3. a) 2158 B) 240
4. a) 50 b) C03
8. 6) 1DCF
9. 6) 190
13. a) — 1 b) —8159
14. a) FDE2 B) 0100
15. - 231
19. Для вычитания дополнительного кода целого числа х из дополнительного кода
целого числа у следует вычесть х из у, полагая их беззнаковыми двоичными числами.
Если в левом разряде необходим <заем», считайте, что его можно сделать.
Глава 4
l.MARGIN=10
2. Счетчик ячеек используется ассемблером во время ассемблирования для слеже-
ния за объемом памяти, в которой хранятся данные и команды. РС используется
CPU во время выполнения программы, чтобы определять следующую выполняемую
команду.
7. (R6) + 16
9. R9 будет содержать 00000А7А.
13. SRADES: . BLKW 20 14. LINE: .BLKB 120
CODES! .BLKB 13 COL1 - LINE+1O
HEADING: .BLKB 11 C0L2 - LINE+25
C0L3 - LINE+40
17. a) MPGMSG: .ASCII '3.785*MILES/LITERS'
Глава 5
3. MESSAGE: .ASCIZ / Какой сегодня день?/
INPUT: 1 1 .BLKB BEGIN 80 DATE 1 Входной буфер
PRINTCHRS MESSAGE 1 Напечатать вопрос
READLINE INPUT 1 Осуществить ввод
PRINTCHRS EXIT .END INPUT.RO DATE 1 Напечатать ответ
6. Одна и та же область ввода может использоваться несколько раз для ввода
строк различной длины, поэтому исходные нули в памяти окажутся <перезаписан- ными».
Глава 6
1. DIVW3 #12,EGGS,DOZENS
8. । Использование регистров! R5.R6 рабочие
MULL3 #7,I,R5 ; 7*1
DIVL3 LL,J,R6 । J/LL
395
ADDL2 R5,R6 9 7#I+J/LL
MULL3 R6,R6,K 1 К - (7*I+J/LL)##2
10. а) Слово, начинающееся с 183A0, содержит 5В83, а регистр R9 — 00148DEA.
в) Байт в 148DE8 содержит FE.
д) Содержимое R7 не изменяется, но код условия переполнения будет установлен.
14. a) ADDW2 #1,R8
19. | Этот программный сегмент вычитает 1 ие каждого четвертого
I элемента в массиве COUNTS слов, начинав с четвертого элемента.
I Число элементов находится в слове NUM. (Полагается, что в мае-
; сиве имеется минимум четыре элемента.)
I
I Использование регистров! R6 указатель массива
R7 счетчик цикла
1 CLRL . • • R7 1 Счетчик должен быть длинным словом
DIVW3 #4,NUM,R7 •1 Число элементов для декремента
1 MOVAW C0UNTS+6,R6 1 Адрес первого изменяемого элемента
DECRi DECW (R6) 1 Декремент элемента
ADDL2 #8,R6 1 Скорректировать указатель массива
SOBGTR R7,DECR 1 Управление циклом
25. a) R3 будет содержать 00029А80, a R4—00029А7С.
29. а) 2D 33 31 30 35 (первый байт находится слева).
30. а) Начиная с PKD, будет запоминаться 17 0D.
35. ) Этот программный сегмент печатает двумерный массив длинных слов.
I Используемые данные!
I
I ARRAY Двумерный массив длинных слов
I ROWS число строк (длинное слово)
I C0LM8 число столбцов (длинное слово)
I
I Предполагается, что элементы имеют максимум 5 значащих цифр и что
I имеется максимум 10 столбцов.
LF - 10 1 Перевод строки
SPACE - 32 1 Пробел
PKD: .BLKB 3
HDGi .ASCIZ <lF>/macchb/<lf>
LINE: .BYTE SPACEC801
396
1 Использование регистров! R6 R7 указатель массива сметчик строк
1 PRINTCHRS HDG MOVAL ARRAY,R6 MOVL ROWS,R7 1 NEWROWi MOVAB LINE,R9 MOVL C0LM8,R8 1 NEXT! CVTLP (R6)+,#S,PKD CVTPS #5,PKD,#5,(R9) ADDL2 #6,R9 MOVB Ф8РАСЕ,(R9)+ SOBGTR R8,NEXT I MOVB *0,(R9) PRINTCHRS LINE SOBGTR R7,NEWR0W R8 R9 1 1 1 1 1 1 1 1 1 1 1 счетчик столбцов укаоатель буфера Инициализировать укаоатель массива Счетчик внешнего цикла Инициализировать укаоатель буфера Инициалиоировать внутренний цикл Преобраоооать в упакованное Передать символьный код в строку Инкремент укаоателя строки Пробел для разделения элементов Управление внутренним циклом Отметить конец выходной цепочки Управление внешним циклом
1. a) N = 0, Z = 0, V=0, C=l, в) 2. a) N=0, Z = 0, V = 0, C = l, в) 3. HERE 11. CMPW WORD,#-100 BL8S NEXT CMPW WORD,#100 BGTR NEXT . CMPB BYTE,#3 BEQL THERE CMPB BYTE,#4 BEQL THERE NEXT! Глава 7 N = 0, Z = 0, V = 0, C = 0. N=l, Z = 0, V = 0, C = 0.
13. CMPL ALPHA+4,BETA+4 BGTR ABC BL88 NEXT CMPL ALPHA,BETA BGTRU ABC 1 Сравнить старшие части
NEXT:
19. Инкремент указателя анализируемого байта производится после каждого срав-
нения благодаря автоинкременту в команде СМРВ, поэтому адрес 'в регистре R6
397
для команды с меткой FOUND является адресом байта, который следует за байтом,
содержащим пробел. Один из вариантов исправления сегмента заключается в том,
чтобы изменить две последние строки:
NOTFNDs CLRL LOCBLANK 1 Пробел не найден
BRB NEXT
FOUNDS MOVAB -(R6),LOCBLANK | Запомнить адрес пробела
NEXTs
26. а) Последовательный поиск: 64; двоичный поиск: 1.
в) Последовательный поиск: 1; двоичный поиск: 7.
28. ADDW2 #7,R9 1 Инкремент индекса
CMPW R9,CNT 1 Сравнить с пределом цикла
BGTR NEXT
BRW CHECK 1 АСВ испольоует смещение-слово Глава 8
В машинном коде первый байт показан справа.
1. а) 54 59 58 С1, в) 65 В6.
2. a) DIVL2 (R7),R9
4. SOBGTR COUNTER,LOOP
7. MOVL 8(R7),R7
8. CMPB 4(R8),#20 1 Сравнить воораст с 20
BLSS NEXT 1 Если меньше, перейти к следующему
CMPB 4(R8),#29 1 Сравнить воораст с 29
BGTR NEXT 1 Если больше, перейти к следующему
CMPB 9(R8), #Z4A/3> r 1 Сравнить Q5 с 3 в коде ASCII
BLSS NEXT
INCL RIO 1 Подсчитать участника
NEXTs ADDL2 #12,R8 1 Скорректировать указатель
12. а) Адреса: 150 14F 14Е
Содержимое: FF 67 CF
в) Адреса: 14F 14E
Содержимое: 80 AF
20. а) Нет ошибки.
в) Назначение перехода слишком далеко для смещения размером в байт.
27. а) ОС В5
Глава 9
1. | ’ПРОЦЕДУРА PRINT.STRINGS (STRINGS,NUM)
Эта процедура печатает массив символьных цепочек.
Входные аргументы
STRINGS массив
NUM число элементов (длинное слово)
398
I Длим» цепочки SIZE определяется в самой процедуре.
I
SIZE - 12
I
.ENTRY PRINT-STRINGS,*M<R6,R7>
1 1 Использование регистров! R6 указатель массива
1 R7 счетчик цикла
1
MOVL 4(AP),R6 1 Указатель массива в R6
MOVL S8(AP),R7 1 Число элементов
PRINT! PRINTCHRS (R6),#8IZE 1 Напечатать цепочку
ADDL2 #8IZE,R6 1 Инкремент указателя
SOBGTR R7,PRINT
RET
₽ND
5. а) Любой порядок является правильным.
в) Нет; команда RET не может определить, сколько регистров запоминалось,
поэтому она не может найти длинное слово, содержащее маску регистров.
8. a) 0000FF6A
10. a) MOVL AP,R10 Указатель кадра для С Указатель кадра для В Указатель кадра для А
в) MOVL MOVL MQVL 12(FP),R1O |
12CR10),R1O |
12(R10),R10 |
16. MOVAL MOVAB MOVL NEXT! PUSHAB PUSHAL ANSWERS,R6 GRADES,R7 «NUM_PEOPLE,R8 (R7)+ | KEY | Адрес для Адрес KEY оценки
PUSHAL (R6)+ I CALLS #3,SCORE | SOBGTR R8,NEXT Глава 1. DELTA = 828 IND = 82E TAG = 836 VOLUME = 838 3. Все действительные, за исключением (г). 4. а) Действительное, переместимое. в) Не действительное. Адрес ответов участника Вызвать SCORE 10 LABEL = 830 . = 844
5. а) 36|б, в) 136|б, д) 6. а) 54ю, абсолютное, 36io. в) 1C616, переместимое.
399
Глава 11
1. .MACRO SWITCH A,В MOVL A,-(SP) । Стак как рабочая область MOVL В,А MOVL (8Р)+,В .ENDM SWITCH
3. Автодекрементный, литеральный
4. .MACRO CVTLQ LONG,QUAD,?LBL CLRL QUAD+4 1 Расширение знака, если > О MOVL LONG,QUAD । Устанавливает коды условий BGEQ LBL MOVL t^XFFFFFFFF,QUAD+4 | Расширение знака, если < О 1 вызывает N«l,ZeO LBLi .ENDM CVTLQ
Фактическим аргументом QUAD должно быть выражение.
16. .MACRO SETREG .IRP N,<0,1,2,3,4,3,6,7,8,9,10 MOVL #N,R*N . ENDR .ENDM SETREG
Глава 12
1 CLRL R9
BISL2 #aX00020120,R9
7. MNEGL R8,— (SP)
BICL2 (SP) + ,R8
11. 23. Поясните, почему эти команды выполняются. а) 53 59 FF 8F 9С Команда ASHL инициирует сдвиг влево, вызывая переполнение. Если преры-
24. вание IV запрещено, будет генерироваться ожидаемая маска (из всех нулей), a) 00003FFC, в) FE00001F.
26. .MACRO CVTLQ LONG,QUAD MOVL LONG,QUAD+4 A8HQ #-32,QUAD,QUAD .ENDM CVTLQ
29. .MACRO UNION SET1,8ET2,DEST,7L0CLBL C'LOCLBL - 0 .REPEAT 8 ВI8L3 8ET1+C•LOCLBL,SET2+C* LOCLBL,DE8T+C * LOCLBL C'LOCLBL - C'LOCLBL+4 .ENDR .ENDM UNION
400
32. INSV #1,NUMBER, #1,R6
39 a) .PSECT HORIZONTAL.LINE
I
I ПРОЦЕДУРА HORIZONTAL.LINE (SCREEN, ROW, RIGHT, LENGTH)
I
I Эта процедура "рисует" горизонтальную линию на двоичной матрице.
I Входные аргументы
I SCREEN двоичная матрица (256«8) длинных слов
I ROW ноМер строки линии (длинное слово)
I RIGHT смещение конца линии от правого края
I жрана (длинное слово)
I LENGTH длина (в Витах) линии (длинное слово)
I Предполагается, что аргументы ужо проворены на достоверность.
I Алгоритм построения!
I posn в* row*2S6 + right}
I while length >- 32 do
I .begin
I вставить 32 единицы, начиная c SCREEN * posn}
I length ! length - 32}
I posn ! posn **>32
I end}
I вставить length единиц, начиная c SCREEN * posn.
•
I Смещения для списка аргументов!
SCREEN - 4
ROW - a
RIGHT - 12
LENGTH - 20
.ENTRY HORIZONTAL_LINE,*M<R6,R7,R8,R9>
! 1 Использование регистров! R6 R7 R8 SCREEN позиция длина (индекс цикла)
i MOVL SCREEN(AP),R6 | 1 SCREEN
MULL3 •ROW(AP),8256,R7 | 1 Номер строки « 256
ADDL2 •RIGHT(AP),R7 । 1 posn ! row«2S8*right
MOVL •LENGTH(AP),R8 | l Длина
1 CMPL R8,#32 | 1 Сравнить длину с 32
BLSS LAST | 1 Пропустить цикл,если меньше
1 INSi INSV •—1,R7,832,(R6) | 1 Вставить единицы
ADDL2 •32,R7 I 1 ровп ! posn * 32
ACBL •32,8-32,R8,INS | 1 Декремент и проверка длины
LAST! INSV 8—1,R7,R8,(R6) | 1 Вставить оставшиеся единицы
RET
.END
401
42 <
PSECT CVTCB
I
| ПРОЦЕДУРА CVTVB (STRING, LONS)
I
J ЗАДАЧА
I
I Эта процедура преобразует символьную цепочку STRING в двоичный набор
I (длинно* слово)'и помещает рваультат в LONS. Предполагается, что це—
I почка содержит коды ASCII шестнадцатеричных цифр и имеет длину в сим-
I волов. Например, цепочка "1234АВCD" преобразуется в двоичный набор
| *X1234ABCD.
I Процедура CVTCB устанавливает код условия Z в P8W, запомненном в
I кадре вызова, если преобразование выполнено успешно. Если любой
I символ в цепочке не является допустимой шестнадцатеричной цифрой,
I Z не устанавливается и в LONS ничего не запоминается.
1 ' - .
I МЕТОД
I
I Каждый символ преобразуется в тетраду, содержащую численное значение
I цифры. Затем тетрада вставляется в правый конец результата и он сдви-
I гается влево на 4 бита, освобождая место для следующей цифры. После
I обработки всех в символов результат необходимо сдвинуть вправо на
I одну тетраду, так как последняя тетрада не должна вызывать сдвига
I ее влево.
.ENTRY CVTCB,*M<R6,R7,RS,R9>
1 1 Использование регистров! R6 адрес следующего символа
1 R7 счетчик цикла
1 RS двоичное значение цифры
1 R9 результат
1 MOVL 4(AP),R6 । Адрес STRING
MOVL #B,R7 । Счетчик цикла
CLRL R9 | Сбросить для результата
DTGITl SUBB3 •AX30,(R6)+,R8 J Получить 0-9
BLS8 RET | Символ < ЛХ30
CMPB R8,69
BLEQ OK
SUBB2 •7,RS | Получить 10 - 1S
BLSS RET | ЛХ39 <символ> ЛХ41
CMPB RS,«15
BGTR RET J Символ > ЛХ46
□Ki BIBB2 RB,R9 | Передать тетраду С
ROTL •4,R9,R9 | Сдвинуть влево
SOBGTR R7,DIGIT
1 ROTL »-4,R9,S8(AP) ) Сдвинуть вправо и Запомнить
BISB2 •*X04,4(AP) | Установить Z в P8W
RETi RET
J .END
402
Глава 13
l .a) —3/8 или —0,375, в) 0.0.
2 . а) 000042С8, в) 00004А80.
7. Команда TSTF проверяет знак в бите 15; команда TSTL проверяет бит 31. Команда
TSTF проверяет биты 15:7 —на нулевые значения; команда TSTW проверяет
биты 15:0.
14. Один разряд. Так как сомножители >1/2, произведение >1/4.
26. .MACRO TSTP NUM DGTS,PKD
MOVB #?Х0С,—(SP) «Упакованный нуль
CMPP NUM DGTS,PKD, #1, (SP)-H Сравнить с нулем
.ENDM TSTP”
Глава 14
2. (RO) =00000000 (Rl) = 00000000 (R3) = LINE+1
6. .PSECT CNT.VOWELS
| ПРОЦЕДУРА CNT.VOWELS (TEXT, LENGTH, VOWELS)
I
I Эта процедура подсчитывает гласные в сегменте текста. Учитываются
I прописные и строчные буквы.
I
I Входные аргументы!
I TEXT текст
I LENGTH число байт в тексте
I
I Выходной аргумент!
I VOWELS счетчики (массив слое)
I
I Массив VOWELS но очищается этой процедурой} ее можно вызывать
I несколько раз для подсчета гласных в различных сегментах текста.
I
TABLE! .BYTE OC6S3,1,0СЗЭ,2,0СЗЭ,3,0С5Э,4,OCS3,5,0С113
.BYTE 1,OC33,2,OC33,3,OC53,4,OC53,5,OC1O3,OC1283
I
I Элементы таблицы являются индексами соответствующих счетчиков
I в массиве VOWELS.
I
I Смещения для списка аргументов!
TEXT - 4
. LENGTH - S
VOWELS - 12
.ENTRY CNT.VOWELS,AM<R2,R3,R6,R7,R8>
1 Использование регистров! RO длина оставшегося текста
Rl адрес найденной гласной
R2-R3 используются командой SCANC
R6 найденная гласная
R7 элемент таблицы (индекс VOWELS)
R8 адрес массива VOWELS
MOVL TEXT(AP),R1 | Адрес TEXT
DECL Rl 1 SCANC использует 1(R1)
MOVL •LENGTH(AP),RO | Взять длину
MOVL VOWELS(AP),R8 | Адрес VOWELS
8UBL2 •2,R8 | V0WEL8-2
403
BCANi 8CANC BEQL CVTSL CVTSL INCH DECL SRB RO , 1 (RD ,TABLE,(TXFF DONE (RD ,R6 TABLECR63.R7 (RS)CR73 RO SCAN 1 Искать гласнук 1 Гласная 1 Эламант таблицы 1 Имкрамамт счетчика 1 Оставшаяся длина
1 DONEi RET
.END
13. Это допустимо; адрес операнда равен ТАВЬЕЧ/ХЗО или TABLE4-48. Команда
пересылает код ASCII пробела в позицию таблицы, соответствующую символу <0».
19. Первый байт->95 00
26. E08L0AD.FILL *
E08FILL 3
EOBMOVE 4
EDaSET.SIGNIF
EOBMOVE 1
E08L0AD.PLU8 < >
E08L0AD.MINU8 C
E088T0RE.8IGN
EOSLOAD,MINUS R
E0B8T0RE.8IGN
ED#FILL 3
EOIEND
Глава 15
1. a) $GET RAB=INRAB t
MOVC3 #80,INBUF,LINE
6) <INPUT:>
Предметный указатель
Абсолютный 236—238, 241
Автодекрементный режим 57—58, 66,
95, 145, 174
Автоинкрементный косвенный режим
163—164, 174
Автоинкрементный режим 56, 66, 95,
115, 145, 174
Адрес 22, 29, 95
Адрес возврата Г83
Адрес перехода 64, 188
Аннулирование 169
Аргументы процедур 189 (см. также
список аргументов; аргументы макро-
команд)
Арифметический сдвиг (см. сдвиг)
Архитектура ЭВМ 17—18, 40
Ассемблер 16—18 (см. также ассемблер
VAX-11 MACRO)
Ассемблер VAX-11 MACRO 233, 235
Ассоциативный закон 320
Атрибут COMMON 231
Байт 18, 22, 26, 53, 56
Байт режима 142, 172
Библиотека процедур VAX-11 времени
выполнения 204—206
Бит 18
Биты выравнивания стека 200
Блок доступа к записи (RAB) 371—373
Блок доступа к файлу (FAB) 370—372
Ввод и вывод 70, 368—377 (см. также
макрокоманды ввода-вывода)
ввод в свободном формате 334, 363
Вершина стека 181, 183
Включение (в стек) 182, 264
Внешний 237—238, 241
Восьмеричная система счисления 40
Время ассемблирования 18—19, 144,
267
Время выполнения 19, 144
Вызов ссылкой 190, 204, 222
Выполнение команд 143, 146, 150, 154,
157
Выравнивание 23. 227
Выражение 23'>. 235, 241
ограничения на выражения 239
Глобальный 188, 238, 241
Граница 22, 227
Графика (см. двоичная матрица)
Данное 18
Двоичная матрица 300—301
Двоичная система счисления 31—35,
40—41
преобразования двоичных и десятич-
ных чисел 33—35
Двоичное поле переменной длины 283,
294—302
Двоичные цепочки 282, 304 (см. также
двоичное поле переменной длины)
Двоичный поиск 119—126
Директива блока неопределенного пов-
торения 267, 271
Директива блока условного ассембли-
рования 268
Директива .ASCII 61, 67
Директива .ASCIZ 61, 67
Директива .BLKx 58—59, 67, 240
Директива .BYTE 59—60, 67, 240
Директива .D FLOATING 314, 335
Директива .DISABLE 237
Директива .DOUBLE 314, 320 (см. так-
же директиву .D___FLOATING)
Директива .END 64, 67, 187, 226
Директива .ENDM 247
Директива .ENTRY 63, 67, 75, 187—189,
222
Директива .ERROR 275
Директива .EXTERNAL 237—238
Директива .F FLOATING 314, 335
Директива .FEOAT 314 (см. также
директиву .F FLOATING)
Директива .G ""FLOATING 314, 335
Директива ,Н FLOATING 314, 335
Директива .IF"268
Директива .IF__FALSE 270
405
Директива .HF 270
Директива .IRP 267, 271
Директива .IRPC 267, 272
Директива .LONG 60, 67, 240
Директива .MACRO 247
Директива .MEXIT 276
Директива .NARG 271
Директива .NOSHOW 253, 271
Директива .PACKED 332, 335
Директива .PSECT 186, 226, 240
Директива .QUAD 60, 67
Директива .REPEAT 267, 272, 353
Директива .SHOW 253, 271
Директива .WORD 60, 67, 240
Директивы ассемблера 51—52 (см. так-
же конкретные директивы)
Дисковая карта 298
Дисперсия 322 .
Длинное слово 23, 29, 56
Длинное слово состояния процессора
(PSL) 27, 30, 169
Дополнение 18, 283 (см. также мно-
жество)
Дополнительный код 40—41, 43—45,
71, 83—84, 331
вычитание 48, 84
изменение знака 43—44
преобразование в(из) символьный
(огр) код(а) 97—102, 131 — 134, 344
сложение 43—46
умножение 85—87
Запись 369
макрокоманды обработки записей 375
Запоминание регистров 187—188
Зарезервированный операнд 99, 166,
312, 315, 331, 363
Защитные биты 319
Знаковый бит 40—41, 47
Извлечение (из стека) 182, 264
Имя 49—51, 66, 241
значение имени 50—51, 66
имя, определяемое пользователем
49—51, 66
постоянное имя 49
Индексные режимы 166—167, 174
Инициализация данных 58, 67, 76, 227
адреса 191, 222
в макрокоманде 252
символа 60—61, 67
упакованных десятичных чисел 332
целых чисел 59—60
чисел с плавающей точкой 313
Исполнимый 19
Исходная программа (исходный мо-
дуль; исходный файл) 18
Кадр вызова 197—198, 202) 222, 285
Ключ 120
Кобол 204—206, 331, 357
Код ASCII 55, 61, 117, 133, 355, 357, 388
Код EBCDIC 351, 355
Код операции 131 —132, 172—173
привилегированной 168
Код операции, зарезервированный фир-
мой DEC 169
Коды условий 83, 109, 112, 135, 200, 203,
284, 342, 362
Команды ввода-вывода 28—29 (см. так-
же макрокоманды ввода-вывода)
побочные эффекты 27
Команды переходов
безусловные 111, 135
условные 109—116, 135
Команды преобразований (см. конкрет-
ные типы данных)
Команды проверки 112—113, 135 (см.
также плавающая точка)
Команды сравнений 112—118, 135
(см. также плавающая точка; упако-
ванное десятичное)
символов 117—118, 135
целых чисел 113, 135
Команды циклов 88—91, 104, 126—
132, 135, 316
Компилятор 18, 346
Конкатенация 251, 279
Косвенные режимы адресации 162—
165, 174
Косвенные режимы со смещением 163,
174, 187,.193
Коэффициент повторения 60
Криптограмма 351—352
Кэш 22
Лексема 346
Лексический анализ 346
Листинг 18—19, 78—79, 159—162, 238,
253—254, 274
Литеральный режим 54, 66, 149—150,
174, 233
символьный 54
с плавающей точкой 320, 335
Логические операции 285—287
Логический сдвиг (см. сдвиг)
Локальная метка 259—261, 279
Локальный 180
Макрокоманда AND 287
Макрокоманда BEGIN 75, 375, 390
Макрокоманда CALL 272
Макрокоманда CVTSL 264
Макрокоманда CVT2S 270
Макрокоманда DUMPLONG 74, 80, 391
406
Макрокоманда EXIT 75, 80, 375, 391
Макрокоманда JJEXIT S 64, 67, 75, 80
Макрокоманда PRINTCHRS 72—73, 80,
390
Макрокоманда READLINE 71, 78, 376,
390
Макрокоманда READRCRD 72, 78—79,
203, 376, 390
Макрокоманды 51—53
аргументы макрокоманд 244, 247, 279
значения по умолчанию 255—257,
262, 279
ключевые слова 255—257, 262, 279
позиционные 255—256, 279
символьная цепочка 257
таблица подстановок (замены) 260
библиотека макрокоманд 252
определение макрокоманд 247—248,
261—262
процессор макрокоманд 244, 279
расширение макрокоманд 244, 252—
254
удобные для пользователей 261—266
функций над цепочками 276—278
Макрокоманды ввода-вывода 70—75,
244—245, 372, 390—391
Мантисса 310, 328, 333
Маска 283, 292, 304, 346 (см. также
регистровая маска)
Маска входа (см. регистровая маска)
Массив 57, 89—90, 93, 107
Машинные команды 51—52
Машинный язык 13—15, 78, 141 — 143,
145, 151, 155
Метка 49 (см. также локальная метка)
метки в макрокомандах 257—258,
273
Метод Горнера 131—134
Механизм дескриптора 197, 205
Механизм непосредственного значения
196, 205
Множество 282, 292—294, 304, 307
дополнение множества 293, 307
объединение множеств 293, 307
пересечение множеств 293, 307
подмножество 293
пустое множество 293, 307
Модуль 18—19
Мультипрограммирование 19, 24, 28
Наносекунда 22
Нарушение доступа 55, 169
Научная нотация 309
Непосредственный режим 54—55, 157—
158, 174, 233
Нормализация 310, 318, 333
Нумерация бит 22, 29
Обратный код 41—42
Общий регистр 26—30
Объективная программа (объективный
модуль, объективный файл) 18—19,
245
Оверлей (наложение) 226, 232
Октаслово '23, 29, 56
Оператор основания 235, 240—241
Оператор прямого присваивания 51,
64, 236, 240
Оператор DO 128—130
Оператор for 128—129, 138
Оператор if 113—116
Оператор repeat-until 127
Оператор while 127
Операторы редактирующего шаблона
357—360, 363
Операторы шаблона (см. операторы
редактирующего шаблона)
Операционная система 20, 25, 28, 30,
70, 183, 222, 282, 369
Операционная система VAX/VMS 20,
369
Основание 32—34, 36, 233
Особый случай 167—170, 173, 224,
314, 335
Особый случай деления на нуль 170,314
Относительный адрес 153—154, 236
Относительный индексный режим 166
Относительный косвенный режим 163—
165, 174
Относительный режим 54, 66, 153—
157, 174, 238—239, 241
Ошибка 168
Ошибка зарезервированного режима
адресации 169
Память 21—23, 29
виртуальная память 24—25, 29
на ферритовых сердечниках 21
основная память 21
полупроводниковая МОП-память 21
физическая память 21
Параметр-значение 205
Паскаль 14, 205—206, 282, 292
Переместимый 236—238, 241
Переполнение 28, 47—48, 103 (см. так-
же плавающая точка; переполнение
десятичной цепочки; прерывание це-
лочисленного переполнения)
Переполнение десятичной цепочки 170,
331
Плавающая точка 309, 333—334
антипереполнение 315, 335
арифметические операции 314, 317—
319, 333—334
двойная точность (D____FLOATING)
310—311, 313, 333
407
диапазон 313
зарезервированный операнд 315, 334
одинарная точность (F_FLOATING)
310—311, 333 .
переполнение 315, 335
представление 309—312
преобразование 315—316
в (из) дополнительный (ого)
код(а) 315, 327—329
в (из) символьный (ого) код (а) 316
проверка 315
сравнение 315
точность 319—320, 322—327, 333—
334
формат G___floating 311, 333
формат Н___floating 311, 333
Подпрограмма 19, 178, 184—185, 222
Позиционное представление 31—32, 46
Поиск (см. двоичный поиск; символь-
ная цепочка; последовательный
поиск)
Поле комментария 49, 65
Поле метки 49, 66
Поле операнда 49, 65
Поле операнда 49, 65
Полином 131 —132
Последний пришел — первый ушел
181 — 182
Последовательный поиск 120, 193—194
Последовательный файл (см. файл)
Представление целых чисел 40—46
(см. также прямой код; обратный
код; дополнительный код)
диапазон 103
критерии выбора 41, 43, 46
Преобразование символьных цепочек
.(см. символьная цепочка)
Прерывание особого случая 168—170
разрешение и запрещение 170
флаги разрешения 188—189, 200—
201
Прерывание прослеживания 170—172
Прерывание целочисленного перепол-
нения 63, 75, 83, 94, 170
Приказ (команда) LINK 81
Приказ (команда) MACRO 80—82
Приказ (команда) RUN 81
Программная секция 226, 240
атрибуты программной секции 226—
227, 240
по умолчанию 229
Программный счетчик (РС) 27. 30, 55,
109, 143, 153, 172—173
использование в выполнении команд
146, 150, 153—154, 157—158
применение при отладке 172
Процедура 19, 178, 222, 245 (см. также
аргументы; список аргументов; кадр
вызова)
408
возврат из процедуры 197—198,
202—203
вызов процедуры 186, 197—202
Процедура обработки условий 168,
200—201, 224
Процесс ассемблирования 144, 150,
152, 155, 158
Прямой код 41, 46—47
Пузырьковая сортировка 138, 177
Пустой указатель 207
Разделитель 61, 257, 338 z
Расширение знака 94, 105
Регистровая маска 186, 188, 200, 202,
233, 240, 264
Регистровый косвенный режим 55—56,
66, 145, 174
Регистровый режим 53, 66, 145, 174
Редактирование текста 338, 349, 363—
365
Редактирование численных данцых
356—363
Редактор связей 19, 153—154, 162,
191, 229, 237, 240—243
Режим перехода 55, 66, 150—152, 174
вычисление смещения 152
Режимы адресации 53, 66, 141, 172
(см. также конкретные режимы)
в макрокомандах 250
Режимы адресации с программным
счетчиком 153, 174 (см. также
непосредственный режим; относи-
тельный режим)
Резервирование памяти 58—59, 67, 75,
225
в макрокоманде 252
Связанный список 206—216
Сдвиг 287, 307
арифметический 287, 290—291 .
логический 287
циклический (кольцевой) 287—288
Семейство PDP-11 311
Символ 22
Символ-заполнитель 340, 358—360
Символ переключения 354
Символьная цепочка 60—62, 67, 76, 97,
240—241, 338—356
команды поиска 342—351, 363—365
преобразования символьных цепочек
и дополнительного кода 97—103
Символьный код 97—103
Системные программы 16
Слово 22, 29, 53, 57
Слово состояния процессора - (PSW)
27, 45, 47, 83, 109, 134, 200, 203, 331
Сдужба управления записями 29, 72,
369—374
Смещенный порядок 309—311, 327
Соглашения ’о связи процедур (см.
стандарт вызова процедур)
Спецификатор операнда 142, 172, 234
Список аргументов 187, 190, 192, 201,
222
Список доступных узлов 208—209
Среднее 322
Стандарт вызова процедур 181, 186—
187; 222
Стандартное отклонение 322
Стек 181—182, 186, 197—202, 222,
263, 265, 279
Стековый кадр (см. кадр вызова )
Стимул 71, 373
Страница 25, 29
Счетчик ячеек (адресов) 49—50, 59,
78, 144, 173, 227, 229, 235
Таблица имен 120, 144—145, 153, 173,
229, 238, 241
Таблица преобразования 351
Терм 232—235
Тетрада 18
Тетраслово 23, 29, 53, 57
арифметические операции 107
Типы данных 25 (см. также плавающая
точка; символьный код; упакованное
десятичное; дополнительный код)
Точка входа 63, 75, 80, 188, 237
Узел 207
Узел-заголовок (головной) 208
Указатель аргументов 26, 30, 193, 200—
202, 222
Указатель кадра 26, 30, 198, 200—202,
223
Указатель стека 26—27, 30, 183, 201 —
202
Унарный оператор 233
Упакованное десятичное. 98—103, 309,
329—333, 335, 357
Управляющий блок 370
Управляющий символ 62
Условное ассемблирование 266—273,
279
Файл 369
индексный 370
относительный 370
последовательный 370
Файл DATA.DAT 71, 74, 77
Файл IOMAC 80, 390
Файл IOMOD 81, 391
Фирма DEC 13
Флаги состояния 203, 264, 372, 374
Флаг значимости 358
Флаг CALLG/CALLS 198, 200, 202
Формальный аргумент блока повторе-
ния 271—272
Форматы команд 141 —143
гибкий формат 141
фиксированный формат 141 —143
Фортран 14, 205, 231, 326, 357
Функция 190
Функция % EXTRACT 277
Функция % LENGTH 276
Функция % LOCATE 277
Целочисленная арифметика 83—87,
103—105 (см. также дополнительный
код)
алгоритмы 83—87
вычитание 84—85
деление 84, 104, 290—291
декремент 87—88
изменение знака 87—88
инкремент 87—88
преобразования 93, 95, 104
сложение 84
умножение 84—86, 290—291
Центральный процессор (CPU) 26—28,
141,^143—146, 150—152, 154
Циклический сдвиг (см. сдвиг)
Шестнадцатеричная система счисления
15, 31—40, 46
арифметические операции 38, 46
преобразование в (из) десятич-
ную (ой) систему(ы). 33—35, 46, 388
преобразование в (из) двоичную (ой)
систему(ы) 32—33, 46
причины применения 38—40, 46—47
Элемент изображения (пиксел) 300
Язык приказов (командный язык) 80
Язык PL/1 357
Оглавление
Предисловие к русскому изданию................................... 5
Предисловие...................................................... 9
Глава 1. Введение .................................................... 13
1.1. Что такое язык Ассемблера и почему необходимо его изучать? 13
1.2. Терминология .............................................. 18
Глава 2. Машинная организация......................................... 21
2.1. Организация памяти и данных................................ 21
2.2. Центральный процессор.................................... 26
2.3. Ввод и вывод............................................... 28
2.4. Заключение................................................. 29
Глава 3. Двоичные и шестнадцатеричные целые числа.................... 31
3.1. Двоичная и шестнадцатеричная системы счисления .... 31
3.2. Представление целых чисел.................................. 40
3.3. Заключение................................................. 46
3.4. Упражнения................................................ 47
Глава 4. Элементы языка Ассемблера.................................... 49
4.1. Имена и метки............................................. 49
4.2. Операторы.................................................. 51
4.3. Адресация операндов........................................ 53
4.4. Резервирование и инициализация областей данных............. 58
4.5. Начало и конец программы................................... 62
4.6. Форматы операторов......................................... 65
4.7. Заключение................................................. 66
4.8. Упражнения ................................................ 67
Глава 5. Простые макрокоманды ввода-вывода............................ 70
5.1. Макрокоманды............................................... 70
5.2. Заключение и команды на выполнение программ................ 78
5.3. Упражнения................................................ 82
Глава 6. Команды манипуляций целыми числами........................... 83
6.1. Общие замечания.......................................... 83
6.2. Арифметические команды................................... 84
6.3. Простая команда цикла и адресация массива................ 88
6.4. Пересылки и преобразования............................... 93
6.5. Преобразования символьного и дополнительного кодов . , . 97
6.6. Заключение................................................. ЮЗ
6.7. Упражнения................................................ 105
410
Глава 7. Переходы и циклы.............................................. 109
7.1. Коды условий и переходы..................................... 109
7.2. Двоичный поиск (пример)..................................... 119
7.3. Команды управления циклами.................................. 126
7.4. Преобразование входных данных в символьном коде (пример) . . 131
7.5. Заключение .1............................................... 134
7.6. Упражнения.................................................. 136
Глава 8. Форматы машинного кода, трансляция и выполнение программы 141
8.1. Общие замечания............................................. 141
8.2. Некоторые регистровые режимы 145
8.3. Литеральный режим . ;................................ 149
8.4. Режим перехода '............................................ 150
8.5. Относительный и непосредственный режимы..................... 153
8.6. Листинг ассемблирования..................................... 159
8.7. Косвенный и индексный режимы................................ 162
8.8. Ошибки времени выполнения................................... 167
8.9. Заключение.................................................. 172
8.10. Упражнения................................................ 175
Глава 9. Процедуры..................................................... 178
9.1. Преимущества использования процедур и проблемы их реализации 178
9.2. Стек........................................................ 181
9.3. Стандарт вызова процедур.................................... 186
9.4. Директива .ENTRY............................................ 187
9.5. Списки аргументов........................................... 190
9.6. Вызов процедуры и возврат управления программе .... 197
9.7. Связь процедур с программами, написанными на языках высокого
уровня и библиотечными процедурами..............................204
9.8. Манипуляции связанными списками (пример).................... 206
9.9. Заключение................................................. 222
9.10 Упражнения.................................................. 223
Глава 10. Некоторые средства языка Ассемблера 226
10.1. Программные секции......................................... 226
10.2. Термы и выражения.......................................... 232
10.3. Типы имен и выражений...................................... 236
10.4. Ограничения на выражения................................... 239
10.5. Заключение................................................. 240
10.6. Упражнения................................................. 242
Глава 11. Макрокоманды................................................. 244
11.1. Общие замечания............................................ 244
11.2. Макроопределения и некоторые примеры....................... 247
11.3. Аргументы макрокоманд...................................... 255
11.4. Локальные метки............................................ 257
11.5. «Дружественные по отношению к пользователю» макрокоманды 261
11.6. Условное ассемблирование................................... 166
11.7. Функции обработки цепочек.................................. 176
11.8. Заключение................................................. 178
11.9. Упражнения................................................. 179
Глава 12. Операции с двоичными разрядами и полями...................... 282
12.1. Общие замечания........................................... 282
12.2. Простые поразрядные операции 283
12.3. Команды сдвигов и циклических сдвигов...................... 287
12.4. Множества (пример)......................................... 292
411
12.5. Двоичные поля переменной .длины............................... 294
12.6. Заключение........................................... . 304
. 12.7. Упражнения.................................................. 305
Глава 13. Числа, представленные в форматах с плавающей точкой и упако-
ванном десятичном....................................................309
13.1. Представление чисел в формате с плавающей точкой ... . . 309
13.2. Операции над числами с плавающей точкой....................... 314
13.3. Непосредственные и литеральные операнды с плавающей точкой 320
13.4. Точность вычислений при расчете дисперсии (пример) .... . 322
13.5. Преобразование числа с плавающей точкой в целое число (пример) 327
13.6. Представление чисел в упакованном десятичном формате . . . 329
13.7. Команды, реализующие операции над упакованными десятичными
числами...................................................... 331
13.8. Заключение........................................... . 333
13.9. Упражнения.................................................... 335
Глава 14. Символьные цепочки.............................................. 338
14.1. Общие замечания....................................: . 338
14.2. Команды MOVC и СМРС........................................... 339
14.3. Команды поиска символов....................................... 342
14.4. Преобразование символьных цепочек............................. 351
14.5. Команда редактирования........................................ 356
14.6. Заключение.................................................... 363
14.7. Упражнения.................................................... 363
Глава 15. Ввод и вывод с использованием RMS................................368
15.1. Операции ввода-вывода........................................ 368
15.2. Служба управления записями.....................................369
15.3. Упражнения.....................................................377
Приложение А. Система команд.............................................. 379
Приложение В. Таблицы преобразования шестнадцатеричных чисел и степе-
ней 2 ............................................................. .389
Приложение С. Символьный код ASCII.........................................390
Приложение D. Макроопределения и процедуры ввода-вывода................... 391
Приложение Е. Ответы на некоторые упражнения...............................395
Предметный указатель.......................................................405
Баазе С.
Б12 Ассемблер мини-ЭВМ VAX-11: Пер. , с англ./Предисл.
В. К. Злобина.—М.: Финансы и статистика, 1988.—413 с.: ил.
ISBN 5—279—00062—0.
Кинга известного американского автора представляет собой практическое руководство,
в котором на примерах программ и упражнений с использованием необходимого справоч-
ного материала раскрываются принципы программирования на языке Ассемблера мини-ЭВМ
VAX-1L Описаны форматы данных, режимы адресации, развитая система команд и основные кон-
струкции Ассемблера. Рассматриваются процедуры, макрокоманды, организация ввода-выврда
и специальные команды.
Для специалистов» занимающихся программированием и применением средств вычислитель-
ной техники, студентов вузов.
2404010000— 064
Б--------------- 127—88
010(01)—88
ББК 24.4.1
Научное издание
Сара Баазе
АССЕМБЛЕР МИНИ-ЭВМ VAX-11
Книга одобрена на заседании секции редсовета по
электронной обработке данных в экономике 31.05.85 г.
Зав. редакцией К. В. Коробов
Редактор Н. К. Логинова
Мл. редакторы Т. Т. Гришкова. Н. Е. Мендрова,
Т. А. Чебакова
Худож. редактор Ю. И. Артюхов
Техн, редактор Л. Г. Челышева
Корректоры Г. А. Башарина, С. Г. Мазурина
И. П. Елкина, Л. Г. Захарко
Переплет художника Безрученкова Л. Е.
ИБ№'1918
Сдано в набор 17.11.87. Подписано в печать 21.04.88.
Формат 60X 88’/i6. Бум. кн.-журн. Гарнитура
«Литературная». Печать офсетная. Усл. п. л. 25,48.
Усл. кр.-отт. 25,48. Уч.-изд. л.- 27,2. Тираж 16 000 экз.
Заказ 821. Цена 2 р. 20 к.
Издательство «Финансы и статистика», 101000, Москва,
ул. Чернышевского, 7.
Московская типография № 4 Союзполиграфпрома при
Государственном комитете СССР по делам' издательств,
полиграфии и книжной торговли
129041, Москва, Б. Переяславская, 46.
ВНИМАНИЮ ЧИТАТЕЛЕЙ!
В 1989 году издательство «Финансы и статистика»
выпустит следующие книги.
Блэнд Г. Основы программирования на языке
Бейсик в стандарте MSX: Пер. с англ.—15 л.: ил.
1 р. 80 к., 20 000 экз.— III кв.
В книге изложен вводный курс программирования
на Бейсике в MSX-стандарте, ориентированном
на 8-разрядные микроЭВМ («Ямаха» и др). Парал-
лельно вводятся основные понятия языка, принципы
устройства ЭВМ, элементы культуры программиро-
вания. Текст иллюстрирован практичными програм-
мами. Рассмотрены расширенные графические (уп-
равление «спрайтами») и звуковые возможности
MSX — Бейсика.
Для всех категорий пользователей микроЭВМ.
Джонстон Г. Учитесь программировать:
Пер. с англ.— 27 л.: ил. 3 р., 30 ОСЮ экз.— II кв.
Книга представляет собой курс лекций профес-
сора К. Хоора по искусству программирования
в интерпретация Г. Джонстона. Методика написания
и анализа программ, выбора алгоритма решения
иллюстрирована программами минимальной слож-
ности, реализованными на языке Паскаль. Является
удачным руководством для систематического освое-
ния начал профессионального программирования.
Для специалистов, постигающих и совершенст-
вующих искусство программирования, преподава-
телей и студентов вузов.