О пользе синонимов
Влад «Bill» Князев
5 мая 2004
Слова-синонимы обогащают язык. Причем не только «человеческие» языки имеют синонимы. Синонимы присутствуют и в языках программирования микроконтроллеров.
Редиска нехороший человек
Вместо предисловия
В толковом словаре русского языка слово синоним определено как: «слово или выражение, совпадающее или близкое по значению с другим словом, выражением». Такое определение этого слова известно всем нам со школьной скамьи и, я думаю, каждому из нас приходилось на уроках русского языка выполнять упражнения, связанные с подбором синонимов к различным словам.
Но задумывались ли вы когда-нибудь о том, для чего нужны синонимы вообще? Какая польза от наличия синонимов для того или иного слова?
На мой взгляд, синонимы дают нам возможность выбирать из множества слов именно то слово, которое наиболее точно, наиболее полно описывает некоторый объект или какие-либо действия. Правильный выбор синонимов позволяет сделать нашу речь, наши мысли более выразительными и, одновременно, более краткими.
Но если вы подумали, что далее речь пойдет о языке человеческом, то, уверяю вас, вы несколько ошибаетесь. Хотя мне и хотелось бы поразмышлять о языке, но о языке компьютерном. Точнее, о языке компьютерных инструкций.
Поводом для этих размышлений послужила одна из дискуссий на форуме microchip.ru. И хотя предметом данной дискуссии было совсем другое, но разговор коснулся и предмета моих нынешних размышлений, которыми мне и хотелось бы поделиться.
Если говорить об мнемонических обозначениях инструкций того или иного процессора, то появление среди них синонимов является таким же обычным явлением, как и наличие синонимов в нашем человеческом языке. Причем синонимы инструкций это объективная закономерность, а не прихоть разработчиков архитектуры.
Но прежде, чем говорить об этих синонимах, нам надо определить, что мы будем понимать под этим термином. А для этого нам нужно понять, каким образом транслятор ассемблера обрабатывает инструкции процессора, директивы самого ассемблера или любые другие конструкции языка.
Немного теории
Обработка исходного текста программы транслятором с языка ассемблера (в дальнейшем просто - ассемблер) происходит построчно. Это означает, что каждая строка исходного текста программы рассматривается как самостоятельное законченное предложение и любые конструкции языка должны быть расположены в пределах одной строки. Недопустимо, скажем, размещать инструкцию процессора на одной строке текста, а ее операнды - на другой.
В общем виде, любая строка исходного текста может состоять из следующих полей:
[<метка>:] [<команда [операнд(ы)]>] [;<комментарии>]
Здесь каждое поле заключено в квадратные скобки [], которые показывают, что данное поле в строке является необязательным и может отсутствовать. Поле <метка> используется в тех случаях, когда имеются ссылки к данной строке из других мест программы. Поле <команда> представляет собой либо инструкцию процессора, либо директиву ассемблера. При этом в качестве операндов могут выступать либо операнды инструкции, либо аргументы директивы ассемблера соответственно. В поле <комментарии> записывается произвольный текст, поясняющий данную конструкцию языка. Данное поле, как правило, является последним в строке и ассемблер прерывает дальнейшую обработку этой строки, когда обнаруживает поле комментариев.
В процессе трансляции программы ассемблер использует несколько таблиц, в которых хранится информация о различных объектах:
- таблица постоянных символов или PST (Permanent Symbol Table);
- таблица символов;
- таблица макроопределений.
В PST хранится информация об инструкциях процессора, директивах ассемблера и о правилах их обработки. Данная таблица создается в момент загрузки программы ассемблера в память, и ее содержимое не может быть изменено в процессе работы ассемблера.
В таблице символов хранится информация о символах, определяемых программистом: имена меток, символические определения констант, информация о программных сегментах и т.д. В момент загрузки ассемблера в память данная таблица является пустой и заполняется информацией в ходе трансляции программы.
Таблица макроопределений содержит информацию об определениях макрокоманд: аргументы макрокоманд, тело макрокоманды и т.п. Данная таблица используется макропроцессором ассемблера по мере необходимости. Так же как и таблица символов, эта таблица при загрузке ассемблера является пустой.
Я не хочу полностью описывать работу ассемблера, в этом нет никакой необходимости. Но мне хотелось бы обратить внимание на обработку ассемблером поля команды, т.е. инструкций процессора или директив ассемблера.
Когда ассемблер в процессе сканирования строки исходного текста обнаруживает некоторый символ, то он должен определить тип этого символа. Для этого ассемблер, прежде всего, просматривает таблицу PST. Если символ обнаруживается в этой таблице, то ассемблер в зависимости от типа символа переходит либо к обработке инструкции с последующей генерацией кода, либо к обработке очередной директивы ассемблера.
Если символ в таблице PST не найден, то ассемблер ищет его в таблице макроопределений. Если этот символ в таблице присутствует, то ассемблер вызывает макропроцессор, который производит обработку найденной макрокоманды. Если символ не найден и там, то он считается неопознанным и ассемблер выдает соответствующее сообщение об ошибке.
Допустим, что ассемблер определил, что данный символ является инструкцией процессора и требуется сгенерировать код данной инструкции. В таблице PST для этой инструкции имеется соответствующий код. Этот код может быть либо полным кодом инструкции, либо кодом поля операции этой инструкции. Остальные поля инструкции формируются в процессе обработки ее операндов, если таковые имеются. Информация о требуемых операндах (их типе, их числе, способы их обработке) также хранится в таблице PST.
Если требуется обработка полей операндов для текущей инструкции, то ассемблер продолжает сканирование исходного текста и определяет требуемые операнды. В результате, процесс обработки инструкций с операндами получается сложнее, по сравнению с обработкой инструкций, не имеющих операндов.
Что же такое команды-синонимы?
Обычно в системе команд процессора имеются отдельные инструкции, которые при некоторых условиях приобретают вполне определенный логический смысл. Причем этот смысл не является абсолютно новым, он просто становится наиболее явственным, более заметным. Часто это происходит тогда, когда соответствующая инструкция общего вида используется с вполне определенными операндами или комбинациями операндов.
В таких случаях, подобная частная инструкция начинает использоваться вполне самостоятельно. А поскольку операнды в этой инструкции предопределены заранее, то имеет смысл определить для частного случая такой инструкции новое мнемоническое обозначение и поместить ее в таблицу PST как новую инструкцию, которая будет являться просто синонимом соответствующей инструкции общего вида.
Особо хочу обратить ваше внимание на то, что команды синонимы размещаются именно в таблице постоянных символов наряду с основными командами. Они не никоим образом не являются макроопределениями.
Возьмем для примера инструкцию BTFSC (Bit Test f, Skip if Clear) из набора команд контроллера семейства PIC16 фирмы Microchip. В общем виде эта инструкция выглядит следующим образом:
BTFSC f, b ; Здесь f - номер регистра в регистровом файле,
; b - номер разряда в регистре
Данная инструкция выполняет проверку определенного разряда b указанного регистра f и пропуск следующей инструкции, если проверяемый бит сброшен. Рассмотрим частный случай этой инструкции:
BTFSC 3, 2
Те, кто не знаком с системой команд данного контроллера, вряд ли увидят какой либо особый логический смысл в этом частном случае. Те же, кто использует этот контроллер, наверняка запомнили, что регистр с номером 3 является регистр состояния (STATUS), а разряд с номером 2 в этом регистре является флажок признака нулевого результата Z. Более понятная эквивалентная запись данной инструкции будет выглядеть так:
BTFSC STATUS, Z ; Пропуск если результат не равен нулю
Правда, в этом случае должны быть определены значения символов STATUS и Z. Но именно сочетание операндов STATUS и Z придает действию команды вполне определенный логический смысл, в соответствии с которым эта команда часто используется. Поэтому вполне логично было бы определить для данного случая специальное мнемоническое обозначение и занести ее в таблицу PST ассемблера:
SKPNZ ; Пропуск, если результат не равен нулю
; (SKIP if Not Zero)
И разработчики ассемблера для микроконтроллеров семейства PIC16 так и поступили, когда ввели вновь полученную «новую» команду в имеющуюся систему команд. В этой команде операнды заданы неявно и трансляция такой команды существенно упростилась.
Но команды синонимы могут быть образованы не только из одной команды, как в нашем примере. В некоторых случаях команда синоним может заменять последовательность из двух или более команд тогда, когда эта последовательность имеет вполне конкретный логический смысл и используется достаточно часто.
Рассмотрим пример такой последовательности команд, часто используемой в программах для PIC16. Как известно, в системе команд PIC16 отсутствуют команды условных переходов. Условное ветвление в программах реализуется с помощью сочетания команд условного пропуска (как рассмотрено выше) и команды безусловного перехода GOTO. Если воспользоваться командой пропуска по ненулевому результату, то условный переход по нулю будет выглядеть так:
SKPNZ ; Переход, если результат равен нулю
GOTO label ;
Такая конструкция из двух инструкций используется очень часто и также имеет смысл объединить эти две инструкции в одну составную команду, которая будет выглядеть более понятно:
BZ label ; Переход, если результат равен нулю
; (Branch if Zero)
Таким образом в систему команд PIC16 были введены команды условных переходов. Конечно, за счет подобных «расширений» системы команд размер программы меньше не станет, такая «новая» инструкция будет занимать по-прежнему два слова. Но наглядность программ, использующих такие инструкции, существенно возрастет.
Могу добавить, что набор таких составных инструкций в ассемблере для PIC16 не ограничивается инструкциями условных переходов. Кроме этих инструкций были добавлены инструкции, связанные с обработкой данных. Полный перечень таких специальных инструкций можно найти в руководстве по ассемблеру.
Хочу отметить тот факт, что использование команд синонимов для PIC16 не является исключением из правил. То же самое можно увидеть в системах команд других процессоров, например, в семействе AVR фирмы Atmel, или в семействе MSP430 фирмы TI. Но и Atmel, и TI, в отличие от Microchip, включили в системы команд своих семейств команды синонимы. Тогда как Microchip по неизвестным мне причинам не посчитала нужным этого сделать. В результате, я думаю, и без того небогатый набор инструкций PIC16 был еще более обеднен.
Кстати, я сам узнал об этих инструкциях только тогда, когда просто из чистого любопытства заглянул в соответствующий раздел руководства. Я не думал найти в этом разделе нечто новое для себя, так как полагал, что все инструкции описаны документации для любого контроллера PIC16. Однако, к счастью для себя я ошибся. И когда я обнаружил эти «новые» инструкции, я не преминул ими воспользоваться, и не без пользы для себя. Но я склонен думать, что далеко не все так любопытны как я, и просто не подозревают об этих командах. А зря.
Команды, которые мы выбираем
Если, несмотря на сделанные выше рассуждения, вы по-прежнему не уверены в пользе использования команд синонимов, то давайте продолжим наши рассуждения.
Во-первых, я думаю, вы успели заметить, что программы, в которых используются синонимы, будут выглядеть более понятными и, вместе с тем, более краткими. Синонимы позволяют уменьшить вероятность ошибок при создании исходного текста программы и ускорить ввод самого текста. Читабельность программы особенно важна в тех случаях, когда предполагается дальнейшее сопровождение данной программы; тем более, когда сопровождать программу будет не ее разработчик, а посторонний программист.
Во-вторых, команды синонимы легче запоминаются. Я, например, отлично помню инструкцию AVR
SEI ; Разрешить прерывания
но для того, чтобы вспомнить общую форму этой команды
BSET 7 ; Разрешить прерывания
; (установить разряд 7 (I) в регистре SREG)
мне пришлось заглянуть в документ, в котором описывается система команд.
В-третьих, команды синонимы обрабатываются ассемблером гораздо быстрее, по сравнению с аналогичными командами, записанными в общем виде, и процесс трансляции займет меньше времени. Конечно, при быстродействии современных компьютеров данное преимущество будет практически незаметно.
В-четвертых, есть еще одно преимущество использования команд синонимов, о котором мне хочется рассказать особо. Для начала попробуйте сравнить команды различных контроллеров: PIC16, AVR и MSP430 соответственно:
MOVF R10, 1 ; PIC16
AND R10, R10 ; AVR
CMP.B #0, R10 ; MSP430
Попробуйте определить, что именно делают данные команды. Я думаю, логический смысл вы сумеете определить не сразу. Человеку, не знакомому с системой команд других контроллеров, помимо того, с которым он работает, сделать это будет тем более затруднительно. Хотя действие этих команд у различных процессоров одно и тоже.
Чтобы понять это, воспользуемся соответствующими командами синонимами:
TSTF R10 ; PIC16
TST R10 ; AVR
TST.B R10 ; MSP430
Теперь вы можете легко понять, что именно делает эта команда. Даже если система команд какого-либо процессора вам незнакома. Данная команда фактически выполняет проверку содержимого регистра (ячейки памяти) R10 на равенство нулю, хотя команды, используемые для этой цели у разных процессоров, реализуются по-разному. При этом одинаковый логический смысл подобных команд приводит к примерно одинаковым мнемоническим обозначениям.
Мне иногда приходилось заниматься переносом программ с одного процессора на другой. И использование подобных команд синонимов в программах существенно облегчает процедуру переноса. В некоторых случаях при переносе было достаточно просто механически отредактировать мнемонику инструкции. А иногда не было необходимости делать даже это.
Вот, пожалуй, и все, что я хотел рассказать о синонимах. Конечно, как вы будете писать: «Редиска» или «Нехороший человек», решать вам. Я свой выбор уже сделал.
© Влад Князев (Bill) 05.05.04