Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

ВВЕДЕНИЕ 6 страница. Примечание Обратите внимание, что программа из примера 2.4 содержит серьезнейшую ошибку



Рис. 2.10. Стековый кадр

Примечание
Обратите внимание, что программа из примера 2.4 содержит серьезнейшую ошибку. В комментариях сказано, что команда обязана иметь длину не более 4 байт, а вся строка вместе с аргументами не более 255. Если программа-клиент на другом конце сокета (сетевого соединения) соответствует RFC822 [RFC822], так оно и будет. Но если программа требованиям этого документа не соответствует, нас ждет беда: нам могут предложить более длинную команду и/или более длинную строку. Последствия, к которым это может привести, будут более подробно разбираться в главе 12.

Но вернемся к стековым кадрам.

Стековые кадры в системе команд SPARC
Микропроцессоры SPARC также не могут обойтись без стекового кадра. Во-первых, не всегда локальные переменные процедуры помещаются в восьми 32-разрядных локальных регистрах. Именно такая процедура приведена в примере 2.4. Во-вторых, нередки ситуации, когда в качестве параметров надо передать по значению структуры, для которых 6 регистров-параметров тоже не хватит. В-третьих, глубина регистрового файла ограничена и при работе рекурсивных или просто глубоко вложенных процедур может исчерпаться. В-четвертых, в многозадачной системе регистровый файл может одновременно использоваться несколькими задачами. Все эти проблемы решаются при помощи создания стекового кадра [www.sparc.com v9].
Для этой цели используются регистры Isp (о6) и %fp (i6). Команда save %sp, -96 %sp делает следующее: она складывает первые два операнда, сдвигает стековый кадр и помещает результат сложения в третий операнд. Благодаря такому порядку исполнения отдельных операций, старый %sp становится %fp, а результат сложения помещается уже в новый %sp.
Самую важную роль стековые кадры играют при обработке переполнений регистрового файла. Регистровый файл SPARC представляет собой кольцевой буфер, доступность отдельных участков которого описывается привилегированными регистрами CANSAVE и CANRESTORE. Окна, находящиеся между значениями этих двух регистров, доступны текущей программе (рис. 2.11). На рисунке показано состояние регистрового файла, в котором текущий процесс может восстановить один стековый кадр (CANRESTORE=1) и сохранить три (CANSAVE=3). Регистр OTHERWIN указывает количество регистровых окон, занятых другим процессом. Регистровое окно w4 на рисунке (обозначенное как перекрытие) занято лишь частично. Текущее окно, частично занятое окно и участки регистрового файла, описанные перечисленными регистрами, в сумме должны составлять весь регистровый файл, так чтобы соблюдалось отношение CANSAVE + CANRESTORE + OTHERWIN = NWINDOWS - 2, Где NWINDOWS- количество окон (на рисунке регистровый файл имеет 8 окон, т. е. 128 регистров).

Рис. 2.11. Регистровый файл SPARC в виде кольцевого буфера. Регистры CANSAVE и CANRESTORE (цит. по [www.sparc.com v9])

Когда же программа пытается сдвинуть свое окно за описанные границы (в ситуации, изображенной на рис 2.11 это может произойти после вызовов четырех вложенных процедур или после возврата из двух процедур — текущей и соответствующей окну w7), генерируются исключительные состояния заполнения окна (window fill) и сброса окна (window spill). При этом вызывается системная процедура, которая освобождает окна из интервала OTHERWIN, сбрасывая их содержимое в стековые кадры соответствующих процедур и при заполнении восстанавливает содержимое принадлежащего нам окна из соответствующего кадра.
В многозадачной системе заполнение и сброс окна может произойти в любой момент, поэтому пользовательская программа всегда должна иметь по стековому кадру на каждое из используемых ею регистровых окон, а указатель на этот кадр должен всегда лежать в %sp соответствующего окна. При этом очень важно, чтобы создание стекового кадра и сдвиг регистрового окна производились одной командой.

Базово-индексный режим

В этом режиме адрес операнда образуется сложением двух или, реже, большего количества регистров и, возможно, еще и адресного смещения. Такой режим может использоваться для адресации массивов — один регистр содержит базовый адрес массива, второй — индекс, откуда и название. Иногда значение индексного регистра умножается на размер операнда, иногда — нет.
На первый взгляд, ортогональные архитектуры должны испытывать определенные сложности с-кодированием такой адресации: для этого нужно два регистровых поля, а большинство остальных режимов довольствуются одним регистром. Однако многие ортогональные архитектуры, например VAX, МС680хО, SPARC реализуют этот режим, пусть иногда и с ограничениями.

Индексный режим адресации VAX
У VAX за операндом, указывающим индексный режим адресации и индексный регистр, следует еще один байт, кодирующий режим адресации и регистр, используемые для вычисления базового адреса (рис. 2.12). Идея разрешить многократное указание индексного регистра в одном операнде, к сожалению, не реализована.

Индексный режим адресации в системе команд SPARC
SPARC позволяет использовать для вычисления адреса в командах LD, зт и JMPL как сумму двух регистров, так и сумму регистра и 13-разрядного смещения. Таким образом, эти команды реализуют либо косвенно-регистровый режим (если используется смещение и оно равно 0), либо косвенно-регистровый режим со смещением, либо базово-индексный режим без смещения. Это, конечно, беднее, чем у CISC-процессоров, но жить с таким набором вполне можно.

Рис. 2.12. Индексный режим адресации VAX

Немало современных процессоров, впрочем, предлагают программисту реализовать такой режим с помощью нескольких команд и с использованием промежуточного регистра, в который следует поместить сумму базового и индексного регистров.

Сложные режимы адресации

реди промышленно выпускавшихся процессоров самым богатым набором экзотических режимов адресации обладает VAX. Кроме всех вышеперечисленных, предлагаются следующие.

Адресация с использованием счетчика команд

Любой процессор предоставляет как минимум один способ такой адресации: адресация самих команд при их последовательной выборке осуществляется при помощи счетчика команд с постинкрементом. У процессоров с командами переменной длины величина постинкремента зависит от кода команды.
Некоторые процессоры позволяют использовать счетчик команд наравне со всеми остальными регистрами общего назначения. Запись в этот регистр приводит к передаче управления по адресу, который соответствует записанному значению. Чтение из этого регистра позволяет узнать адрес текущей команды, что само по себе не очень полезно и часто может быть сделано Другими способами. Однако использование других режимов адресации со счетчиком команд порой позволяет делать неожиданные, но весьма полезные трюки.

Литеральная и абсолютная адресация в PDP-11 и VAX
VAX и PDP-11 не реализуют в чистом виде ни литерального, ни абсолютного режимов адресации. Вместо этого литерал или адрес помещается в программную память непосредственно за операндом и используется, соответственно, косвенно-регистровый с постинкрементом и косвенный с постинкрементом режимы со счетчиком команд в качестве регистра. При исполнении команды счетчик команд указывает на слово, следующее за текущим отрабатываемым операндом (рис. 2.13). Использование постинкремента приводит к тому, что счетчик увеличивается на размер, соответственно, литерала или адреса, и таким образом, процессор находит следующий операнд. Этот остроумный прием можно рассматривать как своеобразный способ реализовать команды переменной длины.

Рис. 2.13. Реализация литеральной адресации через постинкрементную адресацию счетчиком команд

Использование счетчика команд в косвенно-регистровом режиме со смещением позволяет адресовать код и данные относительно адреса текущей команды. Такой режим адресации называется относительным. Программный модуль, в котором используется только такая адресация, позиционно независим: его можно перемещать по памяти, и он даже не заметит факта перемещения, если только не получит управление в процессе самого перемещения, или не будет специально проверять адреса на совпадение. Впрочем, почти такого же эффекта можно достичь базовой адресацией.
Многие современные процессоры такого режима адресации для данных не предоставляют, зато почти все делают нечто подобное для адресации кода. А именно, во всех современных процессорах команды условного перехода используют именно такую адресацию: эти команды имеют короткое адресное поле, которое интерпретируется как знаковое смещение относительно текущей команды.
Дело в том, что основное применение условного перехода — это реализация условных операторов и циклов, в которых переход осуществляется в пределах одной процедуры, а зачастую всего на несколько команд вперед или назад. Снабжать такие команды длинным адресным полем было бы расточительно и привело бы к ненужному раздуванию кода.
Условные переходы на большие расстояния в коде встречаются относительно редко, и чаще всего их предлагают реализовать двумя командами (пример 2.5).

Пример 2.5. Реализация условного перехода с длинным смещением

Beq distant_label; Перейти, если равно
; реализуется как
Bneq $1; Перейти, если не равно
Jmp distant_label
; У команд безусловного перехода обычно используется длинное смещение
; или абсолютный адрес
$1:

Относительные переходы в системе команд SPARC
У большинства CISC-процессоров адресное смещение в командах условного перехода ограничено одним байтом. У SPARC такие команды используют адресное поле длиной 22 бита. С учетом того факта, что команды у SPARC всегда выровнены на границу слова (4 байта), такая адресация позволяет непосредственно указать до 4М (4х220=4 194 304) команд или 16 Мбайт, т.е. целиком адресовать сегмент кода большинства реально используемых программ (рис. 2.14).

Рис. 2.14. Формат команд условного перехода и вызова процессора SPARC

Команда вызова подпрограммы у SPARC также использует адресацию относительно счетчика команд, но адресное поле у нее 30-разрядное и интерпретируется как адрес слова, а не байта. При сложении смещения и счетчика команд возможные переполнения игнорируются, поэтому такой командой можно адресовать любое слово (т. е. любую команду) в 32-разрядном адресном пространстве. На первый взгляд, неясно даже, какая польза от того, что адресация производится относительно счетчика команд, а не абсолютно. Но в 64-разрядных процессорах SPARC v9 польза от этого большая — абсолютный 30-разрядный адрес позволял бы адресовать только первое гигаслово памяти, а относительное смещение адресует именно сегмент кода, в какой бы части 64-разрядного адресного пространства он бы ни находился. Программ, имеющих объем более одной гигакоманды, или даже половины гигакоманды, пока что не написано, поэтому 30-разрядного смещения практически достаточно для адресации в пределах любой современной программы.

Процессоры, не предоставляющие программисту прямого доступа к счетчику команд, зачастую все-таки дают возможность записывать в него произвольные значения при помощи специальных команд вычислимого перехода и вычислимого вызова. Команды вычислимого вызова широко используются для реализации указателей на функции из таблиц виртуальных методов в объектно-ориентированных языках. Главное применение команд вычислимого перехода -- реализация операторов switch языка C/C++ или case языка Pascal.

Банки памяти

Банки памяти используются, когда адресное пространство процессора мало, а приложение требует. При этом стоимостные и электротехнические ограничения позволяют нам установить в систему гораздо больше памяти, чем процессор может адресовать. Например, у многих" микроконтроллеров адрес имеет длину всего 8 бит, однако 256 байт данных, и тем более 256 команд кода для большинства приложений недостаточно. Многие из ранних персональных компьютеров, основанных на 8-разрядных микропроцессорах i8085 и Z80 с 16-разрядным адресом, имели гораздо больше 64 Кбайт памяти. Например, популярные в годы детства авторов компьютеры Yamaha имели до 2 Мбайт оперативной памяти.
Адресация дополнительной памяти в этой ситуации обеспечивается дополнительным адресным регистром, который может быть как конструктивным элементом процессора, так и внешним устройством. Этот регистр дает нам дополнительные биты адреса, которые и обеспечивают адресацию дополнительной памяти. Регистр этот называется расширителем адреса или селектором банка, а область памяти, которую можно адресовать, не изменяя селектор банка, - банком памяти. Значение регистра-селектора называют номером банка.

Банковая адресация в 16-разрядных микропроцессорах
Внимательный читатель, знакомый с системой команд Intel 8086, не может не отметить, что "сегментные" регистры этого процессора имеют мало общего с собственно сегментацией, описываемой в главе 5. Эти регистры более похожи на причудливый гибрид селектора банков и базового регистра. Как и описываемый далее PIC, I8086 имеет команды "ближних" (внутрибанковых) и "дальних" (межбанковых) переходов, вызовов и возвратов.
Относящийся к тому же поколению процессоров Zylog 800 имеет полноценные селекторы банков. Из всех изготовителей 16-разрядных микропроцессоров только инженеры фирмы Motorola осмелились расширить адрес до 24 бит (это потребовало увеличения разрядности регистров и предоставления команд 32-разрядного сложения), все остальные так или иначе экспериментировали с селекторами банков и вариациями на эту тему.

Работа с банками памяти данных обычно не представляет больших проблем, за исключением ситуаций, когда нам нужно скопировать из одного банка в другой структуру данных, которую невозможно разместить в регистрах процессора. Существенно более сложную задачу представляет собой передача управления между банками программной памяти.
В том случае, когда селектор банка программной памяти интегрирован в процессор, предоставляются специальные команды, позволяющие перезагрузить одновременно "младшую" (собственно регистр PC) и "старшую" (селектор банка) части счетчика команд.

Банки команд в Р/С
У микроконтроллеров PIC арифметические операции производятся только над младшими 8 битами счетчика команд, поэтому относительные и вычислимые переходы допустимы только в пределах 256-командного банка. Однако полное — с учетом селектора банка — адресное пространство для команд достигает 64 Кбайт, а у старших моделей и 16 Мбайт за счет использования двух регистров-расширителей. Переключение банка осуществляется специальными командами "длинного" — межбанкового — перехода.

Если банковая адресация реализована как внешнее устройство, проблема межбанковой передачи управления встает перед нами в полный рост. Поскольку мы не имеем команд межбанкового перехода, любой такой переход состоит минимум из двух команд: переключения банка и собственно перехода. Каждая из них нарушает порядок исполнения команд.
Рассмотрим ситуацию детальнее (рис. 2.15): из кода, находящегося в банке 1 по адресу OxlOaf, мы хотим вызвать процедуру, находящуюся в банке 2 по адресу 0x2000. Если мы сначала выполним переключение банка, мы окажемся в банке 2 по адресу ОхЮЬО, не имея никакого представления о том, какой же код или данные размещены по этому адресу. С той же проблемой мы столкнемся, если сначала попытаемся сделать переход по адресу Oxlfff.
В качестве решения можно предложить размещение по адресу Oxlfff в банке 1 команды переключения на банк 2. Возможно, для этого придется переместить какой-то код или данные, но мы попадем по желаемому адресу. Впро-Чем, если мы постоянно осуществляем межбанковые переходы, этот подход Потребует вставки команд переключения банка для каждой возможной точки входа во всех остальных банках. Ручное (да и автоматизированное)размещение этих команд — операция чрезвычайно трудоемкая, и возникает естественная идея: сконцентрировать все эти вставленные команды и соответствующие им точки входа в каком-то одном месте. Впрочем, даже эта идея не дает нам ответа на вопрос, как же при такой архитектуре возвращать управление из процедур? Вставлять команду переключения еще и для каждой команды вызова?

Рис. 2.15. Межбанковый переход

Развитие этой идеи приводит нас к чему-то, похожему на менеджер оверлеев (см. разд. Оверлеи (перекрытия). программный модуль, который присутствует во всех банках по одному и тому же адресу (рис. 2.16). Если нам нужно вызвать известную процедуру в определенном банке, мы передаем ее адрес и номер банка этому модулю, и он осуществляет сохранение текущего банка, переключение и переход. Если процедура делает возврат, она возвращает управление тому же модулю, который, в свою очередь, восстанавливает исходный банк и возвращает управление в точку вызова.
Дальнейшее развитие этой идеи приводит к мысли, что самый простой способ разместить этот код во всех банках — это усложнить схему работы селектора банков, например, всегда отображать первый килобайт адресного пространства на одни и те же физические адреса. Аппаратно это несложно: мы анализируем старшие шесть битов адресной шины процессора. Если они не равны нулю, мы подаем на старшие биты адресной шины памяти содержимое селектора банка, если же равны — нулевые биты. Примерно этим способом и расширяют память большинство микрокомпьютеров на основе 8-разрядных процессоров.
Поскольку мы вступили на путь анализа логического адреса, можно пойти и дальше: разбить адресное пространство процессора на несколько банков, каждый со своим селектором.

Рис. 2.16. Переключатель банков

Адресное пространство PDP-11
Машины серии PDP-11 имеют 16-разрядный адрес, который позволял адресовать 64 Кбайт. У старших моделей серии это пространство разбито на 8 сегментов по 8 Кбайт каждый. Каждому из этих сегментов соответствует свой селектор банка (в данном случае следует уже говорить о дескрипторе сегмента) (рис. 2.17). Физическое адресное пространство, которое может быть охвачено дескрипторами сегментов, составляет 2 Мбайт, что намного больше адресов, доступных отдельному процессу. На первый взгляд, эта конструкция представляет собой усложненную реализацию банковой адресации, цель которой — только расширить физическое адресное пространство за пределы логического, но тот факт, что, кроме физического адреса, каждый сегмент имеет и другие атрибуты, в том числе права доступа, заставляет нас признать, что это уже совсем другая история, заслуживающая отдельной главы (см. главу 5).

Рис. 2.17. Виртуальная память PDP-11/20

CISC- и RISC-процессоры

Часто приходится сталкиваться с непониманием термина RISC, общепринятая расшифровка которого — Reduced Instruction Set Computer (компьютер с уменьшенной системой команд). Какой же, говорят, SPARC или PowerPC -RISC, если у него количество кодов команд не уступает или почти не уступает количеству команд в х8б? Почему тогда х86 не RISC, ведь у него команд гораздо меньше и они гораздо проще, чем у VAX 11/780, считающегося классическим примером CISC-архитектуры. Да и технологии повышения производительности у современных х86 и RISC-процессоров используются примерно те же, что и у больших компьютеров 70-х: множественные АЛУ, виртуальные регистры, динамическая перепланировка команд.
В действительности, исходно аббревиатура RISC расшифровывалась несколько иначе, а именно как Rational Instruction Set Computer (RISC, компьютер с рациональной системой команд). RISC-процессоры, таким образом противопоставлялись процессорам с необязательно сложной (CISC - Complex Instruction Set Computer, компьютер со сложной системой команд), но "иррациональной", исторически сложившейся архитектурой, в которой, в силу требований бинарной и ассемблерной совместимости с предыдущими поколениями, накоплено множество команд, специализированных регистров и концепций, в общем-то и не нужных, но вдруг отменишь команду двоично-десятичной коррекции, а какое-то распространенное приложение "сломается"? А мы заявляли бинарную совместимость. Скандал!
Понятно, что быть "рациональными" в таком понимании могут лишь осваивающие новый рынок разработчики, которых не заботит та самая бинарная совместимость. Уже сейчас, например, фирма Sun, одним из главных достоинств своих предложений на основе процессоров UltraSPARC числит полную бинарную совместимость с более ранними машинами семейства SPARC. Новые процессоры вынуждены поддерживать бинарную совместимость с 64-разрядными младшими родственниками и режим совместимости с 32-разрядными. Где уж тут заботиться о рациональности.
С другой стороны, рациональность тоже можно понимать по-разному. В конце 70-х и первой половине 80-х годов общепринятым пониманием "рациональности" считалась своеобразная (с высоты сегодняшнего дня) ориентация на языки высокого уровня. Относительно примитивные трансляторы тех времен кодировали многие операции, например прологи и эпилоги процедур, доступ к элементу массива по индексу, вычислимые переходы (switch в С, Case в Pascal) при помощи стандартных последовательностей команд. Поскольку все меньше и меньше кода писалось вручную и все больше и больше — генерировалось трансляторами, разработчики процессоров решили пойти создателям компиляторов навстречу.
Этот шаг навстречу выразился в стремлении заменить там, где это возможно, последовательности операций, часто встречающиеся в откомпилированном коде, одной командой. Среди коммерчески успешных архитектур апофеозом этого подхода следует считать семейство миникомпьютеров VAX фирмы DEC, в котором одной командой реализованы не только пролог Функции и копирование строки символов, но и, скажем, операции удаления и вставки элемента в односвязный список. Приведенная в качестве примера шестиадресной команды команда INDEX — реальная команда этого процессора. Отдельные проявления этой тенденции без труда прослеживаются и в системах команд MC68000 и 8086. Аббревиатура CISC позднее использовалась именно для характеристики процессоров этого поколения.
- Другой стороны, неумение трансляторов этого поколения эффективно Размещать переменные и промежуточные значения по регистрам считалось Доводом в пользу того, что от регистров следует отказываться и заменять их Регистровыми стеками или кэш-памятью. (Впрочем, и у VAX, и у MC68000 с Регистрами общего назначения было все в порядке, по 16 штук.)
Ко второй половине 80-х развитие технологий трансляции позволило заменить генерацию стандартных последовательностей команд более интеллектуальным и подходящим к конкретному случаю кодом. Разработанные технологии оптимизации выражений и поиска инвариантов цикла позволяли, в частности, избавляться от лишних проверок. Например, если цикл исполняется фиксированное число раз, а счетчик цикла используется в качестве индекса массива, не надо вставлять проверку границ индекса в тело цикла — достаточно убедиться, что ограничитель счетчика не превосходит размера массива. Более того, если счетчик используется только в качестве индекса, можно вообще избавиться и от него, и от индексации, а вместо этого использовать указательную арифметику. Наконец, если верхняя граница счетчика фиксирована и невелика, цикл можно развернуть (пример 2.6).

Пример 2.6. Эквивалентные преобразования программы

/* Пример возможной стратегии оптимизации. int a
* Код, вставляемый компилятором для проверки границ индекса,
* выделен при помощи нестандартного выравнивания. */
rray[100];
int bubblesort(int size)) int count; do {
count=0;
for(i=l; i<100; i++) {
if (i<0 || i>100) raise(IndexOverflow); if (i-l<0 || i-l>100) raise(IndexOverflow); if (array[i-1]<array[i]) { if (i<0 || i>100) raise(IndexOverflow);
int t=array[i];
if (i<0 || i>100) raise(IndexOverflow); if (i-l<0 || i-l>100) raise(IndexOverflow);
array[i]=array[i-1];
if (i-l<0 II i-l>100) raise(IndexOverflow); array[i-1]=t; count++; I
while (count!= 0);
// оптимизированный внутренний цикл может выглядеть так: register int *ptr=array; register int *limit=ptr; register int t=*ptr++;
if (size<100) limit+=size; else limit+=100;
while (ptr<limit) { if (t<*ptr) { ptr[-l]=*ptr;
*ptr++=t; count++;) else t=*ptr++;
}
if (size>100) raise (IndexOverf low);

По мере распространения в мини- и микрокомпьютерах кэшей команд и данных, а также конвейерного исполнения команд, объединение множества действий в один код операции стало менее выгодным с точки зрения производительности.
Это привело к радикальному изменению взглядов на то, каким должен быть идеальный процессор, ориентированный на исполнение откомпилированного кода. Во-первых, компилятору не нужна ни бинарная, ни даже ассемблерная совместимость с чем бы то ни было (отсюда "рациональность"). Во-вторых, ему требуется много взаимозаменяемых регистров — минимум тридцать два, а на самом деле чем больше, тем лучше. В-третьих, сложные комбинированные команды усложняют аппаратуру процессора, а толку от них все равно нет, или мало.
Коммерческий успех процессоров, построенных в соответствии с этими взглядами (SPARC, MIPS, PA-RISC) привел к тому, что аббревиатура
USC стала употребляться к месту и не к месту — например, уже упоминавшийся Transputer (имевший регистровый стек и реализованный на Уровне системы команд планировщик, т. е. являющийся живым воплощением описанного ранее CISC-подхода) в документации называли RISC-процессором, фирма Intel хвасталась, что ее новый процессор Pentium построен на RISC-ядре (что под этим ни подразумевалось?) и т. д.

Языки ассемблера

Непосредственно на машинном языке в наше время не программирует практически никто. Первый уровень, позволяющий абстрагироваться от схемы кодирования команд, — это уже упоминавшийся язык ассемблера. В языке ассемблера каждой команде машинного языка соответствует мнемоническое обозначение. Все приведенные ранее примеры написаны именно на языке ассемблера, да и в тексте использовались не бинарные коды команд, а их мнемоники.
Встречаются ассемблеры, которые предоставляют мнемонические обозначения для часто используемых групп команд. Большинство таких языков позволяет пользователю вводить свои собственные мнемонические обозначения — так называемые макроопределения или макросы (macros), в том числе и параметризованные (пример 2.7).
Отличие макроопределений от процедур языков высокого уровня в том, что процедура компилируется один раз, и затем ссылки на нее реализуются в виде команд вызова. Макроопределение же реализуется путем подстановки тела макроопределения (с заменой параметров) на место ссылки на него и компиляцией полученного текста. Компиляция ассемблерного текста, таким образом, осуществляется в два или более проходов — на первом осуществляется раскрытие макроопределений, на втором — собственно компиляция, которая, в свою очередь, может состоять из многих проходов, смысл которых мы поймем далее. Часть ассемблера, реализующая первый проход, называется макропроцессором.

Пример 2.7. Пример использования макроопределений

; Фрагмент драйвера LCD для микроконтроллера PIC
; (с) 1996, Дмитрий Иртегов.
; Таблица знакогенератора: 5 байт/символ.
; W содержит код символа. Пока символов может быть
; только 50, иначе возникнет переполнение.
; Scanline содержит номер байта (не строки!)
; Сначала определим макрос, а то устанем таблицу сочинять.; Необходимо упаковать 7 скан-строк по 5 бит в 5 байт.
CharDef macro scanl, scan2, scan3, scan4, scanS, зсапб, scan7; Следующий символ
RetLW (scan? & Oxlc)» 2
RetLW ((scan5 E, 0x10)» 4) + ((зсапб S Oxlf) «1) + ((scan7 & 0x3) «
6)
RetLW ((scan4 & Oxle)» 1) + ((scanb & Oxf) «4)
RetLW ((scan2 & 0x18)» 3) + ((зсапЗ & Oxlf) «2) + ((scan4 & Oxl) «7)
RetLW (scanl & Oxlf) + ((scan2 & 0x7) «5)
endm
FetchOneScanline IFNDEF NoDisplay
ClrF PCLATH
AddWF PCL, 1
NOP; else
RetLW 0 endif
; А вот идет собственно таблица:
Nolist
; О
CharDef В'OHIO',В110001',В'10001',В'10001',В'10001',В'10001',В'01110'; 1
CharDef В'00100',В'01100',В'00100',В'00100',В100100',В'00100',В'01110'; 2
CharDef В'OHIO',В'10001',В'00001',В'00010',В100100',В'01000',В'11111'; 3
CharDef В'01110',В'10001',В'00001',В'00110',В100001',В110001',В'OHIO'; 4
CharDef В'00010',В'00110',В'01010',В'10010',В'11111',В'00010', В'00010'; 5
CharDef В'11111',В'10000',В'11110',В'00001',В'00001',В110001',В1OHIO'; б
CharDef В'OHIO1,840001',В' 10000',В' НПО',В' 10001',В'10001',В'OHIO1; 7
CharDef В'11111',В'00001',В'00010',В'00100',В'01000',В'01000',В'01000'
; 8
CharDef В'OHIO', В'10001',В'10001',В'OHIO', В'10001', В'10001',В'OHIO'; 9
CharDef В'OHIO', В'10001', В'10001',В'01111',В'00001',В'10001',В'OHIO' 4 Зэк Х(,
Constant CharacterA = Oxa
CharDef В ' 00100 ', В ' 01010 ', В ' 10001', В110001',841111', В' 10001',В' 10001' Ifndef NO_ALPHABET; В Constant CharacterW = Oxb
CharDef В'11110',В'10001',B'10001',В'11110',В'10001',В'10001',В'11110' else; Р — для аона
CharDef В' 11110 ',840001', В '10001', В '10001', В' 11110 ',840000', В '10000' endif; С
CharDef В'01110',В'10001',В'10000',ВЧОООО',В'10000',В110001',В'01110'; о
CharDef В' 11110 ',В' 10001 ',В' 10001 ',В' 10001 ',840001', В' 10001 ',841110'; Е
CharDef B'lllll',ВЧ0001', В'10000', В'11110 ',В'10000', В'10001',В'11111'; F
CharDef В'11111',В'10001',В'10000',В'11110',В'10ЮОО',В'10000',В'10000'
; пробел Constant SPACE_CHARACTER = 0x10
CharDef В'00000',В'ООООО',В'ООООО',В'00000',В'00000',В100000',В'00000'
Constant DASH_CHARACTER = Oxll
CharDef В'00000',В'00000',В'00000',В'11111',В'00000',В'ООООО',В'ООООО'
List





Дата публикования: 2014-11-18; Прочитано: 571 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.01 с)...