Поиск

Полнотекстовый поиск:
Где искать:
везде
только в названии
только в тексте
Выводить:
описание
слова в тексте
только заголовок

Рекомендуем ознакомиться

'Документ'
Мы начинаем традиционную сессию, посвященную новым трендам в законодательстве, регулировании и налогообложении нефтегазодобычи на шельфе и шире, на Во...полностью>>
'Решение'
На гашеную известь, взятую в необходимом количестве, подействовали 3,15 г чистой азотной кислоты. Какую массу нитрата кальция Са(NО3)2 получили, если ...полностью>>
'Документ'
О приватизации земельного участка, который находится в пользовании у гражданки Посоховой Т.В. для строительства и обслуживания жилого дома, хозяйствен...полностью>>
'Документ'
Одна из функций семьи – первоначальная социализация ребенка. Однако семья живет в определенном обществе, в определенное время, эпоху. Все стороны (пер...полностью>>

Главная > Документ

Сохрани ссылку в одной из сетей:
Информация о документе
Дата добавления:
Размер:
Доступные форматы для скачивания:

4. Ошибки в программе

Ошибки в программах могут быть двух типов: синтаксические и алгоритмические. В первом случае в программе просто написано что-то такое, чего компилятор не может разобрать и, соответственно, не знает, как превратить это в коды инструкций. Во втором случае программа компилируется нормально, но делает что-то не то, чего хотел программист, потому что он или сам не знал, чего хотел, или же не смог логически правильно выразить это на языке программирования. Ошибки второго типа мы не будем даже обсуждать – это абсолютно бесполезно, хотя это самые тяжелые и неприятные ошибки. О некоторых ошибках первого типа мы постараемся получить представление сейчас.

Запишем программу такого вида:

ldi r16,1

ldi r15,5 ; этого нельзя делать с регистрами с 0-го по 15-й

ldi r17,2

ldi r19,1000 ;такое число не влезет в однобайтовый регистр

add r16,r17

add r16,4 ;нельзя прибавить непосредственное значение

mov r18,r16

blaha muha ;это вообще не инструкция.

Сохраним ее в файл (например, prg2.asm) и попробуем скомпилировать. в результате мы увидим следующие замечания от компилятора:

Creating 'prg2.hex'

Assembling 'prg2.asm'

prg2.asm(8) : error : Unknown instruction opcode

prg2.asm(2) : error : Illegal argument type or count

prg2.asm(6) : error : Illegal argument type or count

Assembly complete with 3 errors

Deleting 'prg2.hex'

То есть компилятор нашел ошибки во 2-й и 6-й строке, связанные с использованием неправильных аргументов (операндов) для правильных инструкций, а также в 8-й строке неправильную инструкцию вообще. После этого он заявляет "ассемблирование закончено с 3 ошибками" и пишет о том, что выходной файл prg2.hex был удален.

Относительно ошибки во 2-й строке нужны пояснения. Команда LDI позволяет загружать числа только в регистры с номерами с 16-го по 31-й. Чтобы загрузить непосредственное число в один из младших 15 регистров, необходимо загрузить его сначала в какой-нибудь из старших, а оттуда командой MOV скопировать уже куда надо.

Ошибка в 6-й строке связана с тем, что прибавлять к регистру можно только значение из регистра. Непосредственное значение прибавлять нельзя (в некоторых процессорах можно, там для этого есть специальная инструкция ADI).

Закомментируем эти три строки с ошибками, поставив в начале "точку-с-запятой", чтобы компилятор их не видел, и повторим компиляцию. Она завершится без ошибок, хотя мы-то знаем, что по крайней мере еще одна ошибка есть в четвертой строке. Откроем файл prg2.hex в отладчике, чтобы посмотреть, во что превратилась ошибочная команда. Вместо "ldi r19,1000" мы увидим "ldi r19,0xE8". Компилятор понял нашу инструкцию так, что от числа 1000 надо взять 8 младших разрядов (остаток от деления на 256) и записать их в регистр. Что важно – нас он о такой ошибке не предупредил!

5. Переходы в программе

Переведем с Паскаля еще одну программу.

текст на Паскале:

текст на ассемблере:

коды инструкций:

label

do_it_again;

var

i:byte;

begin

i=0;

do_it_again:

i=i+1;

goto do_it_again;

end.

clr r16

inc r16

rjmp 1

$2700

$9503

$CFFE

Что это за команды?

CLR (Clear – очистка) записывает в регистр ноль – у этой команды только один аргумент.

INC (Increment – увеличение) прибавляет к регистру единицу (тоже один операнд).

RJMP (Relative Jump – относительный прыжок). С этой командой мы и будем разбираться подробнее. Как видим, аргументом команде задано число 1 – это номер ячейки памяти программ (адрес инструкции) куда мы хотим перейти. То есть мы желаем вернуться к 1-ой инструкции, содержащей команду "inc r16" (вспомним, что адреса инструкций начинаются с нуля).

Слово "относительный" и буква "r" в названии инструкции нас могут особенно не волновать, однако поясним: они означают что в кодовом представлении этой инструкции зашифровывается не абсолютный адрес перехода, а смещение относительно текущего – положительное или отрицательное. В данном случае в коде $CFFE этой инструкции записано число –2, то есть после выполнения инструкции из нового значения счетчика инструкций процессор вычтет 2, так что следующей инструкцией окажется первая. С этим связано ограничение: инструкция rjmp может прыгнуть не дальше, чем на 2048 слов вперед или назад по коду – дело в том, что величина смещения зашифровывается в команде 12 битами, которыми можно представить только такие числа.

Было бы неудобно постоянно запоминать адреса нужных инструкций. Хуже всего, что при вставке или удалении нескольких инструкций адреса бы, конечно, менялись. В связи с этим создатели компилятора ассемблера придумали метки. Рассмотрим ту же программу с участием

метки:

текст на ассемблере:

коды инструкций:

clr r16

do_it_again:

inc r16

rjmp do_it_again

$2700

$9503

$CFFE

Это уже больше похоже на Паскальный оригинал, хотя программа на самом деле не изменилась ни на один байт!

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

Поэтому при дизассемблировании hex-файла в эмуляторе вы никаких меток, конечно, там не найдете. Скомпилируйте программу, откройте ее в симуляторе и потрассируйте ее, чтобы увидеть как работает переход, организуя цикл с увеличением значения регистра №16 на единицу каждые два такта.

Таким образом метка является как бы подстановочным термином, определяемым пользователем. Можно считать, что ассемблер перед компиляцией просматривает входной файл, составляет словарь подстановочных терминов, и заменяет все вхождения таких терминов нужными значениями или выражениями. Собственно на процесс преобразования мнемоник инструкций в коды подстановочные термины не влияют. Прежде чем двинуться дальше, рассмотрим другие виды подстановочных терминов.

6. Макроподстановки в ассемблере

Часто для удобства программиста полезно бывает заменить, например, название регистра или какое-нибудь число на более понятное слово. Иногда это просто улучшает читаемость программы, иногда это бывает необходимо для облегчения модификации программ. Посмотрим наш первый пример, переписанный с такими "удобствами".

var

a,b,c:byte;

begin

a:=1;

b:=2;

c:=a+b;

end.

.def VAR_A=r16

.def VAR_B=r17

.def VAR_C=r18

.equ VALUE_A=1

.equ VALUE_B=2

ldi VAR_A,VALUE_A

ldi VAR_B,VALUE_B

add VAR_A,VAR_B

mov VAR_C,VAR_A

$E001

$E012

$0F01

$2F20

Мы видим, что для определения подстановочных терминов используются директивы компилятора – выражения, не влияющие на код самой программы, но управляющие работой компилятора. В данном случае директивы DEF и EQU заставляют компилятор записать кое-что в таблицу подстановочных терминов. Названия происходят от слов Define (определить) и Equivalent (равно).

Обратим внимание, что подстановочные термины для имен регистров определяются директивой DEF, а для чисел – директивой EQU.

Важно понять что такие макроподстановки не являются определением переменных или констант, как в Паскале. Для них не выделяется память и вообще они не изменяют конечного кода программы. Они все будут заменены в тексте программы еще до компиляции (однако текст во входном файле, конечно, не будет перезаписан).

Основных преимуществ от использования подстановочных терминов два:

- во-первых мы можем, если будем думать головой, назначить регистрам или каким-нибудь значениям "говорящие" имена, например, если вы назовете число 314 именем PI_100, то в дальнейшем вы легко догадаетесь, что имелось в виду число "пи" умноженное на 100;

- во-вторых, если, например, значение числа "пи" умноженного на 100 было употреблено в программе в 10 разных местах, а на следующий день вышел декрет правительства, что умножать нужно было не на 100, а на 0100 (то есть в восьмеричной системе, всего 64) нам легко будет изменить это значение переписав лишь саму макроподстановку; так же и с регистрами – если однажды нам покажется, что регистр № 16 неудобен для использования в качестве хранилища значения A (потому что мы использовали его еще для чего-нибудь), мы можем просто вписать в макроподстановке имя другого регистра, а не лазить по всей программе "поиском и заменой".

Отметим еще забавную особенность. Теперь исходный текст нашей программы составляет 154 байта, а компилируется он в 4 слова, то есть всего в 8 байт. Вообще с программами на ассемблере такое бывает часто – пишешь полдня, а результат малюсенький.

6. Слово состояния (флаги процессора) и условные переходы

Вернемся к рассмотрению программы с циклом. Сам по себе такой цикл бесполезен, потому что он безвыходный (хотя в программах для контроллеров это встречается часто) – попав в него контроллер уже никогда не выберется обратно. Чтобы избежать этого в Паскале мы обычно каждый раз в конце цикла проверяем какое-нибудь условие и осуществляем переход только если оно выполняется (или не выполняется). Посмотрим, как это происходит в ассемблере.

текст на Паскале:

текст на ассемблере:

коды инструкций:

label

do_it_again;

var

i:byte;

begin

i=0;

do_it_again:

i=i+1;

if i<10 then

goto do_it_again;

end.

.def REG_I=r16

.equ MAX_I=10

clr REG_I

do_it_again:

inc REG_I

cpi REG_I,MAX_I

brlo do_it_again

$2700

$9503

$300A

$F3E8

Первые две инструкции – очистка регистра и прибавление единицы – остались такими же, как и прежде. У нас они вопросов не вызывают, хотя мы и ввели теперь два подстановочных термина – но поскольку мы разобрались, что это только лишь текстовые замены, мы совершенно не переживаем по этому поводу. Можно лишь отметить что некоторые программисты предпочитают писать подстановочные термины большими буквами, чтобы легче отличать их от всего остального. Ну это не так важно.

Однако что дальше? Мы видим, что инструкция проверки условия и перехода "if...then goto" превратилась в две отдельные инструкции. Много напрягая мозги можно догадаться, что по-видимому инструкция CPI каким-то образом сравнивает регистр r16 с числом 10, а инструкция BRLO осуществляет переход на команду по адресу 1. Однако очевидно, этот переход выполняется не всегда, а только если результат сравнения был удовлетворительным.

Как же обмениваются информацией эти две команды?

Откроем в симуляторе hex-файл с нашей программой. Сразу обратим внимание, что при дизассемблировании команда BRLO получила немного другое название – просто у комадны, выполняющей данное действие (мы скоро узнаем, какое) есть несколько разных мнемоник, которые, в общем, правильно описывают ее назначение. Дизассемблер попросту не знает, как мы записали эту команду в исходном тексте, однако поскольку никакой разницы нет, его это не волнует. И нас волновать не должно.

Откроем окно состояния процессора и станем трассировать программу. Мы увидим, что при выполнении некоторых команд в восьми маленьких клеточках с общей надписью Flags появляются или исчезают галочки. Путем мучительных раздумий можно догадаться, что это восемь бит одного специального регистра процессора – регистра флагов или, как его еще называют, слова состояния процессора (Status Register, SREG и т.п.). Дело в том, что записью нулей и единичек в биты этого регистра процессор отмечает для себя кое-какие важные вещи и впоследствии, при выполнении некоторых инструкций он может проверять состояние этих "флажков" и выполнять эти инструкции по-разному в зависимости от того, что он там увидел.

Теперь все просто. Инструкция CPI (Compare with Immediate – сравнить с непосредственным значением) сравнивает значение в указанном регистре с указанным значением и в зависимости от результата устанавливает различные флажки в слове состояния. Фактически эта инструкция вычитает из регистра заданное число, только результат никуда не записывает, а лишь устанавливает в соответствии с характером этого результата нужные флаги (см. ниже).

Инструкция BRCS (Branch if Carry-flag is Set – перейти если флаг переноса установлен, то есть равен единице) проверяет флажок переноса (он отмечен буквой C в окне симулятора, это самый младший бит) и осуществляет переход на указанный адрес, если этот флажок установлен. Флаг переноса означает что при предыдущей команде вычитания (или сравнения) первое число оказалось меньше второго и нам пришлось "занять" как мы это делаем, вычитая в столбик. Поскольку предыдущей командой была команда CPI, это в данном случае означает, что значение в регистре №16 оказалось меньше 10.

И вот, поскольку BRCS осуществляет переход если предыдущая команда сравнения показала, что первый операнд меньше второго, эта команда также называется BRLO, что расшифровывается как "Branch if Lower" – перейти, если меньше. Поэтому-то у этой инструкции две мнемоники. В одних случаях мы хотим пояснить что нас интересует собственно флаг Carry, в других случаях мы имеем в виду, что нам интересен результат сравнения чисел. По сути же выполняется одно и то же действие.

Поясним еще значение некоторых флагов. Флаг переноса кроме вычитания участвует еще в операциях сложения. Как нетрудно предвидеть, он устанавливается если в результате сложения двух чисел (размером по одному байту) они оказываются слишком большими и необходимо перенести единичку в следующий (восьмой, если считать с нуля) разряд. Например если складывать $85+$B9=$13E – единица "вылезает" за пределы байта. Она-то и помещается в флаг переноса. Специальная команда ADC может складывать два регистра и прибавлять к результату эту самую единичку из флага переноса (если она там есть) – это позволяет удобно суммировать многобайтные числа в несколько инструкций. Есть также инструкция перехода, если флаг переноса сброшен (равен 0) – BRCC (Branch if Carry-flag is Cleared) – она также известна как BRSH (Branch if Same or Higher – перейти, если числа равны или первое больше) по тому действию, которое она выполняет после инструкции сравнения чисел.

Следующий флаг, первый бит (считая с 0) в служебном регистре SREG – флаг нуля, поэтому он и обозначен буквой Z. Он устанавливается если в результате предыдущей операции получился 0, поэтому, например, он устанавливается в самом начале программы при выполнении инструкции CLR. С его участием тоже можно выполнять условные переходы – дело в том, что после инструкции сравнения он устанавливается в 1 если числа были равны (тогда в результате их "виртуального" вычитания получается именно ноль) – поэтому инструкция записывается мнемоникой BREQ (Branch if Equal – перейти, если числа равны). Противоположная по смыслу инструкция, совершающая переход если флаг нуля сброшен (числа не равны) называется BRNE (Branch if Not Equal).

Следующие флаги – отрицательности (N), переполнения (V), знака (S) и полупереноса (H) также устанавливаются большей частью в арифметических и некоторых других операциях. Шестой бит (T) это пользовательский флаг – специальной инструкцией в него можно скопировать любой бит из любого регистра а потом записать его в какой-нибудь другой бит другого регистра. Кроме того есть инструкции (BRTS и BRTC), совершающие переход в зависимости от состояния этого флага.

Последний флаг (I) специальный – он разрешает или запрещает выполнение процессором процедур прерываний по сигналам.

Все флаги можно установить или сбросить специальными инструкциями SE* и CL*, где вместо звездочки подставляется буква, обозначающая нужный флаг.

7. Служебные регистры

Кроме 32 регистров общего назначения процессору микроконтроллера доступны еще 64 служебных регистра. Запись информации в них, или чтение информации оттуда позволяет общаться с различными устройствами микроконтроллера. Например в служебных регистрах 4 и 5 хранится значение, показывающее какое напряжение было измерено аналого-цифровым преобразователем, регистр номер $17 содержит 8 бит, показывающих, включены ли соответствующие 8 ножек микросхемы на ввод логических сигналов или на вывод (когда они подключены на вывод логических сигналов, к ним можно с огромным интересом присоединять светодиоды, например), а самый старший служебный регистр номер $3F – это как раз наш знакомый регистр состояния процессора, содержащий флаги.

Для записи информации в служебные регистры используются специальные команды IN и OUT. Их названия связаны с тем что такие регистры вообще говоря не являются регистрами процессора, а внешние по отношению к нему – в старинных компьютерах они были в виде отдельных микросхем, припаиваемых к общей шине данных и адресов.

Команда IN принимает в качестве операндов имя общего регистра (от r0 до r31) и номер служебного регистра. Например "in r16,$2F" запишет в 16-й регистр число из служебного регистра номер $2F – этот регистр хранит текущее значение встроенного таймера.

Команда OUT принимает такие же операнды, но в обратном порядке. Например команда "out $1D,r5" запишет число из регистра №5 в служебный регистр номер $1D – этот регистр используется для записи значений в перепрограммируемое ПЗУ данных. Например если мы сделали на основе контроллера лабораторный стенд, нам будет полезно записывать в ПЗУ пользовательские настройки перед выключением этого стенда, или, например, хранить там таблицу синусов.

Однако, каждый раз вспоминать и писать номера служебных регистров неудобно – кроме того, они могут быть разными у контроллеров различных типов. Очевидно, было бы удобно использовать для "обзывания" этих регистров подстановочные термины. К счастью, существуют готовые файлы со списками таких макроподстановок. Например в пакет AVR Studio они входят – это файлы с расширением "inc". Чтобы "подключить" такой файл к своей программе, нужно просто написать в ней строчку вроде:

.include "c"

Когда компилятор дойдет до такой строки, он откроет указанный файл, прочитает также его, как будто бы это была часть нашего файла, запомнит макроподстановки, если они там есть, и потом продолжит компилирование нашей программы. Разумеется, чтобы этот файл нормально открылся, он должен лежать в том же каталоге – в противном случае нужно прописать полный путь к нему.

Например в файле с указанным названием записаны имена для служебных регистров контроллера Tiny15 – причем эти имена написаны здесь так же, как в руководстве ATtiny15.pdf. Что чрезвычайно удобно.

Мы будем использовать служебные регистры в первую очередь, чтоб изменять напряжения на ножках микроконтроллера. Даже у самых маленьких 8-ногих микроконтроллеров под землю, питание и сброс заняты только три ноги. Остальные 5 контактов могут быть использованы в произвольных целях. Например у ATtiny15 эти пять ножек отвечают 5 младшим битам "порта B". Порт B (бэ-латинское) обслуживается целыми тремя служебными регистрами, из которых нас интересуют два.

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

Если порт включен на выход, то напряжения на ножках контролируются служебным регистром номер $18 (он называется PORTB). Если в какой-то бит записана единица, на соответсвующей ноге появляется 5 Вольт. Если ноль – появляется ноль.

Вот пример программы, включающей 5 вольт на ножке PB0 и 0 Вольт на ножке PB4.

текст на ассемблере:

коды инструкций:

.include "c:\atmel\c"

ldi r16,0b00010001

out DDRB,r16

ldi r16,0b00000001

out PORTB,r16

$e101

$bb07

$e001

$bb08

Можно скомпилировать эту программу и залить ее в контроллер, чтобы проверить (вольтметром или светодиодом) что она действительно работает.

Другой вариант – использовать симулятор VMLAB, который позволяет конструировать несложные виртуальные схемы с использованием светодиодов, резисторов, выключателей и микроконтроллера. О нем мы поговорим позже.

Сейчас же заметим, что собственно изучение ассемблера – это только половина изучения программирования для микроконтроллеров. Как видим, еще нужно знать как служебные регистры позволяют управлять ресурсами микроконтроллера. Исчерпывающая информация по этому поводу представлена в официальных руководствах фирмы ATMEL, которые в виде PDF-файлов удобно скачиваются, например, с и которые требовалось попробовать переводить в качестве первого задания.

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



Похожие документы:

  1. В. В. Бибихин Витгенштейн: смена аспекта М.: Институт философии, теологии и истории св. Фомы, 2005. 576 с

    Документ
    ... записей ... включен в бесконечное пространство оттенков. Теперь мы можем ... их пример В. бы в Трактате привел. До первой публикации он послал ... выполнить ... , мы получаем в ... программный автомат никак не отреагирует, ес- ли человек за пультом начнет ... впоследствии ...
  2. Давайте проведем небольшой тест. Какие ассоциации вызывает у вас слово «хакер?»

    Документ
    ... памяти самомодифицирующийся программный код. Он мне ... Ну, мы можем сделать ... После этого, он собирал в кучу все подпрограммы и объединял их в одну, после чего получалось то, чего он ... возможно впоследствии создавал ... процессор — 6502, мог теперь выполнять ...
  3. Новые педагогические и информационные технологии в системе образования под редакцией д-ра пед. наук проф. е.сполат

    Документ
    ... выполнять, но они должны 39 знать, какие могут быть роли (лучше записать их ... • получите возможность проанализировать пример циклового пла ... язык команд, память, процессор и некоторые ... кода HTML, что, конечно, по силам далеко не всем ученикам), но мы можем ...
  4. И. П. Беляев проектирование

    Документ
    ... , что мы можем любую деятельность нарисовать с помощью квадратиков, потом нажать кнопочку, и получить работоспособное ... , процессором и программными средствами, позволяющими редактировать и обрабатывать данные независимо от работы ЭВМ. Примерами ИТ ...
  5. Велосипедный туризм ббк 75. 81 В27

    Документ
    ... Но мы можем проследить ... он должен быть включен. В результате такой съемки можно получить ... память компьютера, обору-дованного соответствующим аппаратным и программным ... записи и помет-ки в пути можно заносить в отдельный блокнот. Маршрутная схема выполняется ...

Другие похожие документы..