Вы находитесь на странице: 1из 10

2-1/3. Особенности организации памяти программ ПИКов среднего семейства.

Определение "места дислокации" большого массива байтов данных и способа


его обработки. Принцип организации переходов между блоками памяти
программ, с использованием операторов.

Итак, имеется код картинки 1-го "кадра" (1024 байта).


Нужно вывести эту "массивную" картинку (через ее код) на индикацию.
То есть, "архинеобходимо состряпать" некую программу, в которой все байты кода
картинки, "покристалльно, постранично и побайтно", были бы "переправлены" в
графический модуль.
Казалось бы, ничего особо уж сложного в этом нет.
Ан нет, "ларчик так просто не открывается".
Вопрос: "Почему"?
Ответ: (по совокупности рассуждений): "сразу только кошки родятся" (Остап Бендер).
Сначала нужно как следует помучиться в том смысле, что "новорожденные" имеют
природное обыкновение "писаться и какаться", и пока "в эксплуатацию не будет введен
горшок", придется заняться этим, хотя и не очень-то приятным, но почетным и
жизнеутверждающим делом.
Резонно … И возразить-то нечем.
Голос из-за кулис: "А как на счет халявы"? (очередной "камикадзе нарисовался")
Ответ: никак. Точка. Без труда … (и т.д. Всем известно).
В свое время, Александр Милевский все это глубоко прочувствовал, осознал, и как
следствие этого, "попахал" на славу.
Его "карапуз" уже не только на горшок ходит, но и в туалет.
То есть, этот "карапуз" достиг такой степени зрелости, что "родитель" вполне может
им гордиться.
Мне же остается только "влезть в шкуру" Александра и повторить "цепочку"
осознанных действий, отработка которой привела к успеху.
"Влезаю"…
Начну издалека, с "разложения по полочкам".

Особенности организации памяти программ ПИКов среднего семейства.

Вся память программ ПИКов среднего семейства разделена на страницы памяти


программ.
"Объем" каждой такой страницы составляет
256х8=2048
слов EEPROM-памяти программ (далее - просто "память программ", без EEPROM).
По всей видимости, главной причиной выбора именно такого "объема" страницы памяти
программ является то, что команды переходов call и goto имеют 11-разрядную
адресацию.
Вот Вам и число 2048 (2 в 11-й степени).
В зависимости от типа ПИКа среднего семейства, количество страниц может быть
разным: от одной до четырех.
Например, в PIC16F873A, две страницы.
Относительно не сложно догадаться, что, с целью "запуска в работу" содержимого той
или иной ячейки памяти программ (а проще говоря, команды), каким-то образом
должна быть обеспечена возможность адресного обращения к содержимому этих
"ячеек".
Предостережение: не следует путать слово памяти программ и слово адреса ячейки
памяти программ.
Слово памяти программ это содержимое ячейки памяти программ (14 двоичных
разрядов).
Слово адреса ячейки памяти программ это ее (ячейки) адрес (13 двоичных
разрядов).
Таким образом, должен иметь место быть некий счетчик команд, посредством которого
и осуществляется адресация к той или иной "ячейке" памяти программ (выбор
исполняемой команды, с последующим "запуском ее в работу").

1
Он и есть.
Счетчик команд состоит из двух регистров: регистра старшего байта счетчика
команд и регистра младшего байта счетчика команд (двухбайтный регистр).
256 - число состояний, которое способен отобразить один байт, все биты которого
задействуются.
Вот Вам и регистр младшего байта счетчика команд , все триггеры которого, в ходе
адресации, задействуются.
Байт, который "лежит" в этом регистре, называется младшим байтом счетчика
команд.
Стандартное название младшего байта счетчика команд - "PCL".
Стандартное название регистра младшего байта счетчика команд - "регистр PCL".
От выбора того или иного числового значения PCL, зависит выбор адреса той
или иной (из 256-ти) ячейки памяти программ.
8 - число блоков памяти программ.
В каждом блоке памяти программ, находится "набор адресов", состоящий из 256-ти
адресов ячеек памяти программ.
Вот Вам и "епархия" регистра старшего байта счетчика команд.
Байт, который "лежит" в этом регистре, называется старшим байтом счетчика
команд.
Стандартное название старшего байта счетчика команд - "PCH".
Стандартное название регистра старшего байта счетчика команд - "регистр PCH".
В этом регистре, активны не все триггеры.
Это связано с тем, что используются не все биты PCH.
А именно: не используются биты с №№ 5, 6, 7. В них всегда "лежат" нули.
Младшие три бита PCH, с №№ 2, 1, 0, способны отобразить 8 состояний.
Число блоков памяти программ тоже равно восьми.
Вывод: биты PCH c №№ 2, 1, 0 задают номер текущего блока памяти программ
(одного из 8-ми).
То, что Вы прочитали, "локализовано" внутри одной страницы памяти программ.
Предположим, что команд переходов нет и программа отрабатывается только за счет
автоинкремента счетчика команд (исполняется "строго линейно").
В этом случае, после того, как все команды страницы памяти программ будут
последовательно отработаны, произойдет адресный переход на начало этой же
страницы памяти программ.
То есть, будет иметь место быть работа по кольцу.
Чтобы этого не допустить и обеспечить дальнейшую, "безбяковую" работу программы
(если она имеет соответствующий, "внушительный объем"), нужно осуществить
адресный выбор следующей страницы (перейти на следующую страницу).
"За это дело отвечают" биты PCH с №№ 4, 3 (адресный выбор страницы памяти
программ).
Так как их 2 штуки, то можно адресоваться к 4-м страницам памяти программ.
А больше и не нужно.
Если объем памяти программ ПИКа составляет 2 страницы (например, как у
PIC16F873A) и менее, то используется только бит с №3, а бит с №4 не используется.
Вывод: биты PCH c №№ 4, 3 задают номер текущей страницы памяти программ.
Итак:

1 Биты PCH c №№ 4, 3 задают номер текущей страницы памяти программ.


2 Биты PCH c №№ 2, 1, 0 задают номер текущего блока памяти программ.
В адресных "границах" текущего блока памяти программ, числовое значение
3 PCL задает адрес той ячейки памяти программ, которая содержит
исполняемую команду.

"Копаю" дальше.
Регистр PCL доступен для чтения/записи (отображается в области оперативной памяти
ПИКа).
С регистром PCH сложнее.
Во-первых, регистр PCH, напрямую, не доступен для чтения/записи (не отображается
в области оперативной памяти ПИКа).
2
Но ведь как-то работать с ним нужно?
Вот разработчики и придумали дополнительный ("посреднический") регистр PCLATH и
"прописали" его в области оперативной памяти ПИКа.
Обращение к регистру PCLATH соответствует обращению к регистру PCH.
Короче, самый натуральный "переводчик беседы двух иностранцев".
Во-вторых, регистр PCH является регистром старшего байта счетчика команд, и
поэтому приращение числового значения PCH на единицу приводит не к "скачку" на
следующую команду, а к "скачку" через 256 команд.
И это должно быть понятным, ведь происходит выбор следующего блока памяти
программ.
А ведь можно прирастить и не на единицу, а например на 2, 3 и т.д.
В этих случаях, "скачек" будет кратен 256.
Если в регистре PCH "циркулируют" числа от 00h до 07h, то работа будет
происходить в пределах 1-й страницы памяти программ (ее еще называют нулевой, но
мне такая нумерация не нравится).
Если в регистре PCH "циркулируют" числа от 08h до 0Fh, то работа будет
происходить в пределах 2-й страницы памяти программ.
И т.д., вплоть до 4-й страницы включительно (если ТТД ПИКа позволяют).
Для определения адресных "координат" той или иной команды программы, удобно
пользоваться окном ROM:

Например, команда, выделенная на этой картинке, в памяти программ, "лежит" в 6-м


блоке (см. 05h) 1-й страницы (1355-я команда).
Числовое значение PCL (адрес внутри блока) = 4Ah.

А на этом рисунке, выделенная команда (в данном случае, "пустышка"), в памяти


программ, "лежит" во 2-м блоке (см. 09h) 2-й страницы (2376-я команда).
Числовое значение PCL (адрес внутри блока) = 47h.

А теперь об особенностях, связанных с переходами/возвратами.


Сначала - приятное:
О возврате по стеку.
При возврате по стеку (завершает процедуру, переход в которую происходит по
команде call или по команде типа виртуальный call), в счетчик команд, из вершины
стека, переписывается "полномасштабный", 13-разрядный адрес команды, на которую
осуществляется возврат.
Это означает то, что вернуться можно, например, с 4-й страницы памяти программ на
1-ю, не прибегая при этом к манипуляциям с содержимым регистра PCLATH.
Короче, нет никаких проблем, связанных с разделением на страницы и блоки.
Ну и океюшки.
А теперь - не слишком приятное:

О командах переходов call и goto.

3
Ранее было отмечено, что команды переходов call и goto имеют 11-разрядную
адресацию, которая позволяет им работать только по всему объему одной страницы
памяти программ.
То есть, с помощью этих команд, можно "прыгнуть" на любую команду текущей
страницы памяти программ (но не на соседнюю страницу!!!).
"Прыгнуть-то" конечно можно (почему бы и не "прыгнуть", если можно?), а что
дальше?
А вдруг этот "прыжок" произошел не в "епархии" текущего блока памяти программ, а
через "границу"/"границы" блоков памяти программ?
В этом случае, значение PCL изменяется "достойным образом", а вот значение PCH
"будет вести себя не так достойно".
Оно "будет вести себя просто отвратительно" в том смысле, что не изменится
(останется тем, какое оно было до этого "прыжка").
Если, после такого "сюрприза", программа будет исполняться далее (а куда она
денется?), то "целомудренная и добродетельная рабочая точка программы улетит в
такой дремучий и глухой лесок, в котором она будет изуверски изнасилована (очень
вероятен фатальный исход)".
Такого "безобразного надругательства над родным человеком" допустить никак нельзя.
Значит, нужно "перестрелять всех надругателей, а заодно и лесок спалить (уничтожить
среду обитания. Чтобы одни пеньки остались)".
Вот этим-то и займусь немного ниже.

А пока вернусь к своим "недожаренным котлетам".


Имеется 1024 байта данных (большой массив байтов), которые нужно поместить не в
оперативную, а в энергонезависимую память.
Какого именно типа?
Варианты:

Внешнее ПЗУ параллельного типа.


1 Можно. Но для этого нужно очень сильно себя не уважать, ведь это "прошлый
век", плюс, "куча" задействованных выводов портов и необходимость
организации процедуры обмена данными между ПИКом и внешней памятью.
Внешняя EEPROM память последовательного типа.
2 Уже легче (в смысле количества задействованных выводов портов), но во всем
остальном, "хрен редьки не слаще".
EEPROM память данных ПИКа.
3 Заманчиво, но не подходит, так как ее объем оставляет желать гораздо
большего.
EEPROM память программ ПИКа.
4 А вот это именно то, что нужно. Современные ПИКи "это дело вполне потянут"
(и даже "место" останется, если подойти с умом).

Вывод: "дурь выбивать" нужно из EEPROM памяти программ ПИКа, что, в


переводе на русский, означает: массив байтов кода "кадра" нужно поместить в память
программ ПИКа.
Каким образом?
Ведь можно так "поместить", что проще "повеситься"…
Александр Милевский пришел к следующему, достаточно закономерному и разумному
выводу:
в случае "сплошного" заполнения обеих кристаллов (то есть, дисплея), нужно
использовать табличные, вычисляемые переходы (с использованием директивы DT),
количество которых равно числу заполняемых страниц
(то есть, 8x2 кристалла = 16-ти).
Примечание: возможность или невозможность альтернативы этому будет рассмотрена
позднее, а пока занимаюсь тем, что есть.
После этого, "неизбежно и автоматически рождается" двуединая проблема, связанная с
переходами между блоками памяти программ, суть которой состоит в следующем:

4
- при переходе (call, goto) через "границы", разделяющие блоки памяти
программ,
- и в ходе исполнения процедуры вычисляемого перехода, если таблица
вычисляемого перехода расположена не в одном, а в двух (или более)
блоках памяти программ,
изменяется только числовое значение PCL, а числовое значение PCH
не изменяется.

То есть, в этих случаях, числовое значение PCH остается таким, каким оно было
на момент исполнения команды перехода.
Ранее, о наличии такой проблемы было сказано, но сказано "вскользь" и не
достаточно полно, так как я, руководствуясь народной мудростью типа "всему свое
время", при организации всяческих переходов, сознательно старался не выходить за
пределы текущего блока памяти программ.
А вот теперь это время настало, так как, в данном случае, когда имеется большой
массив байтов данных, который в один блок не "втиснешь", эту проблему "на кривой
козе не объедешь".
Так как сия проблема является двуединой, то в соответствии с многократно
повторяемой "концепцией" (представляю, как я Вам надоел своим занудством), ее
нужно "расчленить" на две части и "раздолбать" их по-отдельности.
Далее, "продукты этой долбежки" нужно функционально "состыковать" друг с другом,
"напильником убрать рогатости, пошкрябать надфильком, любовно погладить шкуркой (и
т.д., вплоть до бархотки и нежного поцелуя)".
Именно таким образом, "откуда не возьмись", и возникает трудное, конструкторское
счастье (без иронии).
Что-то я расчувствовался не по делу.
Вспомнил, как будучи пацаном, смастерил бумажного змея (это тоже каким-то боком
относится к конструированию), и он летал.
Вот счастья-то было …
Сейчас, конечно же, все по-другому (скоро полтинник и грехов тьма-тьмущая), но
иногда перепадает.
Слезу пустил … Достаточно. А теперь ближе к делу.

1. Переходы между блоками памяти программ с использованием команд


call и goto.
На первый взгляд, решить проблему достаточно просто: нужно, сразу же после
осуществления перехода (лучше всего), соответствующим образом скорректировать
числовое значение PCH.
С техническим аспектом этой коррекции (через регистр PCLATH), никаких проблем нет.
Остается только разобраться со словосочетанием "соответствующим образом".
Далее, как джин из бутылки, появляется вопрос: "Какое число записывать в PCH"?
При этом, следует учесть, что:
- переходов достаточно много,
- они могут быть "разбросаны" в тексте программы различными способами,
- изменения, вносимые в текст программы (например, в ходе ее "доведения до ума"),
приводят к изменениям адресов ее команд в памяти программ.
Если, при таком "раскладе", действовать стандартно, то возникают весьма
существенные сложности в работе с теми корректирующими константами, на величину
которых нужно изменить текущее значение PCH.
В простейшем и самом неэффективном случае, можно определить эти значения,
например, с помощью окна ROM (нет проблем), а затем осуществить соответствующие
"PCH-привязки".
Но если, после этого, будет иметь место быть смещение текста программы (например,
за счет добавления или удаления команд) или перестановка ее составных частей, то
вероятнее всего, это приведет к нарушению части ранее осуществленных,
фиксированных "PCH-привязок" (со всеми вытекающими, "гробовыми" последствиями).
Получается, что каждый "чих" чреват пренеприятнейшими последствиями и достаточно
трудоемкой работой по их устранению ("египетский труд").
Хорошо это или плохо?
5
А это с какой стороны посмотреть.
Это хорошо (и даже отлично) тогда, когда нужно преподнести неожиданный "сюрприз"
(от "Африканского Деда – Мороза") тем людям, которые "несанкционированно
вознамерятся" что-то изменить в тексте Вашей программы.
"Плеваться" и ругать автора всяческими нехорошими словами будут даже те люди,
которые в курсе проблемы.
Об остальных даже и не говорю.
Но в данном случае, это плохо (и даже отвратительно).
В данном случае, такие "сюрпризы" не нужны.
Но что взамен?
Для того чтобы ответить на этот вопрос, нужно, опять же, начать издалека.
Итак, имеется стандартный способ коррекции числовых значений PCH.
Так как речь идет об установке какого-то фиксированного (заранее определенного)
"набора" корректирующих констант, то в голове "всплывает" весьма отдаленная
аналогия с фиксированной задержкой.
Но ведь есть еще и "плавающая" задержка.
А почему бы не предположить, что можно решить поставленную задачу, "привязав
глобальную суть" "плавающей" задержки к предмету разговора?
С учетом этого, поставленную задачу можно достойным образом решить, если будет
иметь место быть некий программно-аппаратный "механизм" адресной "адаптации" к
всяческим изменениям местоположений тех команд, на которые осуществляются
переходы.
Значит, нужно обратить внимание на метки, выставленные на этих командах и найти
тот программно-аппаратный "механизм", с помощью которого, после обращения к метке,
можно было бы определить и "задокументировать" числовое значение PCH команды,
на которую указывает метка.
Примечание: это могут быть и не метки, а названия подпрограмм, но в дальнейшем я
буду ориентироваться на метки (просто это слово более короткое).
А дальше все просто: исходное, числовое значение PCH нужно заменить на
числовое значение PCH выбранной метки.
Назову-ка я такой способ коррекции содержимого PCH "плавающим" способом
коррекции содержимого PCH, а стандартный, привычный способ коррекции
содержимого PCH - "фиксированным" способом коррекции содержимого PCH.
Это для устранения двусмысленности.
Итак:
"Фиксированный" способ коррекции содержимого PCH: применяются константы,
значения которых определяет и задает программист.
"Плавающий" способ коррекции содержимого PCH: указанные выше константы, не
применяются. "Новое" значение PCH определяется значением PCH команды, на
которой выставлена метка.

Последнее равносильно утверждению, что переход через "границы" блоков памяти


программ, с помощью команд переходов, приводит к изменению значения PCH.
Но оно совсем не соответствует действительности: такого изменения нет по
определению.
Вопрос: "Как сделать так, чтобы это утверждение оказалось верным"?
Ответ: нужно "заставить" его стать верным.
Вопрос: "Как заставить"?
Ответ: нужно как следует "принюхаться" и отыскать, упомянутый выше, программно-
аппаратный "механизм".
В результате его использования и будет осуществлена "трансформация" того
"некрасивого", что имеет место быть по определению, в то "красивое", что нужно.
Как Вам эта, совсем не "хилая", задачка?
Александр Милевский блестяще с ней справился.
Он "откопал" арифметические операторы high и low, с помощью которых можно
решить не только проблему переходов во всяческие блоки памяти программ (PCH/high),
но и проблему обеспечения инкремента содержимого PCH, в случае, если исполнение
сценария вычисляемого перехода связано с "перескоком границы" между блоками
памяти программ (PCL/low).
6
Теперь необходимо разобраться с понятием "операторы".
Чем "операторы" отличаются от "директив" и "макросов"?
В очень общем и предельно упрощенном виде, это отличие таково:
Директивы это "заменители" команд в тексте программы.
"Из-за спины" той или иной директивы, "торчат уши" команды или "набора" команд той
или иной функциональности, которую изменить нельзя (задана разработчиками).
Макросы это те же директивы, но с возможностью изменения функциональности по
желанию программиста.
Операторы, это то, что осталось от всего этого "великолепия".
Операторы представляют собой довольно-таки "разношерстную толпу", отдельные
"члены" которой, в той или иной степени, "тяготеют" либо к директивам, либо к
макросам, либо вообще "черт знает к чему".
Короче, "Ноев ковчег", где "каждой твари по паре".
Большая часть операторов относится к арифметическим операциям.
Причем, арифметические операции в десятичной и двоичной (логические операции)
системах исчисления, как бы, "свалены в одну кучу".
В их честь, всю эту "толпу" назвали арифметическими операторами.
В рассматриваемом случае, "арифметика" не нужна.
Для начала, нужно скопировать, в регистр W ("в каждой бочке затычка"), содержимое
старшего байта адреса команды, на которую указывает метка.
На "микрочиповском языке", это соответствует фразе "возвращение старшего байта".
Вот, собственно говоря, это и есть достаточно незамысловатый итог "вышележащих
словоблудий".
После этого, можно "вытворять", с содержимым W, "все что угодно".
Такое копирование производится с использованием оператора high.
В общем и упрощенном виде (предполагается, что вся таблица "дислоцируется" в
одном блоке памяти программ), это выглядит так:
.....................
.....................
call TEXT_1 ; Переход в ПП TEXT_1.
;---> Возврат по стеку из ПП TEXT_1
.....................
.....................

TEXT_1 movlw high STR_1 ; Установка в PCH числового значения старшего


movwf PCLATH ; байта адреса (в PC) первой команды ПП/метки
; STR_1 (через W).
.....................
;--------------------------------------
; Табличный, вычисляемый переход.
;--------------------------------------
addwf PCL,F ; Приращение PCL на величину содержимого W.
STR_1 dt 0x05,0x00,0xFE,0x02,0x02,0xFE,0x00,0x00
dt 0x00,0x00,0xFC,0x24,0x24,0x18,0x00,0x00
.....................
.....................

Предположим, что после исполнения команды call TEXT_1, произошел переход,


например, с 1-го (PCH = 00h) в 7-й (PCH = 06h) блок памяти программ.
При этом, значение PCH не стало равным 06h, а так и осталось равным 00h.
В вычисляемый переход, с таким "вопиющим безобразием", входить никак нельзя (для
тех, кто "смерти ищет", можно).
Поэтому, ПП TEXT_1 начинается с "наведения порядка" в PCH.
Происходит обращение к старшему байту адреса той команды retlw … (вспомните про
разложение директивы DT на команды), на которой выставлена метка STR_1
(movlw high STR_1).
В данном случае, это команда retlw 0x05 (1-й сценарий вычисляемого перехода).
Она имеет PCH = 06h.
В результате исполнения команды movlw high STR_1, именно это число и запишется в
регистр W (W = 06h).
7
Далее следует стандартное обращение к регистру PCH, через дополнительный регистр
PCLATH (movwf PCLATH).
Дело сделано: в регистр старшего байта счетчика команд записано числовое значение
старшего байта адреса команды 1-го сценария вычисляемого перехода.
После этого, можно смело в него входить.

2. Организация работы табличного, вычисляемого перехода в случае, если его


таблица расположена в двух блоках памяти программ.

Ну хорошо. "Безбяково" вошли в процедуру вычисляемого перехода.


Далее, имеются 2 новости: одна хорошая, а другая плохая (чтоб ей пусто было).
Начну с хорошей новости.
Если таблица вычисляемого перехода не разделена на две части "границей" блоков
памяти программ (как в недалеком прошлом были разделены ФРГ и ГДР), то все
будет ОК.
Плохая новость: если "граница" блоков памяти программ находится внутри таблицы
вычисляемого перехода, то сценарии вычисляемого перехода, расположенные выше (по
тексту программы) этой "границы", будут "безбяково" отработаны, но вот с отработкой
тех сценариев, которые расположены ниже "границы", будут большущие проблемы.
Проще говоря, программа просто "даст дуба". И "дуба капитальнейшего".
Вывод: нужно сотворить нечто такое-эдакое, после введения которого, стало бы
абсолютно "по барабану", проходит ли вышеупомянутая "граница" внутри таблицы
вычисляемого перехода или не проходит.
Это уже "епархия" оператора low.
Александр Милевский сделал это благое дело следующим образом:

.....................
.....................
call TEXT_1 ; Переход в ПП TEXT_1.
;---> Возврат по стеку из ПП TEXT_1
.....................
.....................

TEXT_1 movlw high STR_1 ; Установка в PCH числового значения старшего


movwf PCLATH ; байта адреса (в PC) первой команды ПП/метки
; STR_1 (через W).

;--------------------------------------
; Анализ выхода или нет за пределы
; текущей страницы памяти программ.
;--------------------------------------
movf Reg,W ; Номер сценария вычисляемого перехода --> W.
addlw low STR_1 ; Суммирование номера сценария и числового
; значения младшего байта адреса (в PC)
; ПП STR_1 (через W).
;---------------------------------------------
; Проверка на наличие или отсутствие переноса
; (PCH+1 или этого делать не нужно?).
;---------------------------------------------
btfsc Status,C ; Перенос был или нет?
incf PCLATH,F ; Если был, то PCLATH+1 (PCH+1=...).
movf Reg,W ; Если не был, то инкремента содержимого PCH
; не происходит и номер сценария вычисляемого
; перехода (в любом случае) копируется в W.
;--------------------------------------
; Табличный, вычисляемый переход.
;--------------------------------------
addwf PCL,F ; Приращение PCL на величину содержимого W.
STR_1 dt 0x05,0x00,0xFE,0x02,0x02,0xFE,0x00,0x00
dt 0x00,0x00,0xFC,0x24,0x24,0x18,0x00,0x00
.....................
.....................

8
Использована та же "заготовка", с помощью которой объяснялась работа оператора
high (см. выше), но с "дальнейшим расширением и углублением" (выделено красным
цветом).
После установки нужного значения PCH (повторяться не буду), ранее установленное (в
том месте, где .......) число номера сценария вычисляемого перехода (всего
сценариев - 64 штуки) копируется в регистр W (movf Reg,W).
При этом, содержимое регистра номера сценария (Reg) не изменяется. Это важно.
Далее используется оператор low.
С его помощью, производится суммирование числа номера сценария вычисляемого
перехода и числа младшего байта адреса команды 1-го сценария вычисляемого
перехода, которая помечена меткой STR_1 (addlw low STR_1).
По результату этого суммирования можно сделать вывод о том, проходит или нет,
через таблицу, "граница", разделяющая блоки памяти программ.
Выше этой "границы" или при ее отсутствии, будет иметь место быть суммирование
без переноса, а ниже ее будет иметь место быть суммирование с переносом.
Остается только проанализировать состояние флага переноса-заема (бит С регистра
Status) и определиться, в ходе исполнения какого именно сценария, нужно
инкрементировать содержимое PCH ?
Должно быть понятным (я надеюсь на это), что инкремент содержимого PCH должен
произойти только в случае наличия переноса (работа ниже "границы").
После инкремента содержимого PCH, обязательно нужно скопировать, из регистра Reg,
в регистр W, число номера сценария вычисляемого перехода.
Если этого не сделать, то в регистре W будет "лежать" результат суммирования и
получится "Гитлер капут".
Если, в результате суммирования, переноса не произошло (работа выше "границы" или
она вообще отсутствует), то инкремента содержимого регистра PCH не происходит
(обход команды инкремента), в регистр W копируется номер сценария вычисляемого
перехода и, как говорится, в добрый путь ("бяки" не будет).
Задача решена "малой кровью".
"Снимаю шляпу". Прекрасная и эстетичная работа.
Лично я, от осознания этого, получил удовольствие.

Об особенностях возврата по стеку.


Предположим, что отдельный сценарий вычисляемого, табличного перехода отработан
"без сучка и задоринки".
Теперь нужно вернуться по стеку "туда, откуда пришел".
С "механизмом" возврата по стеку проблемы нет.
Возврат будет осуществлен именно в то место, которое нужно ("строптивого" возврата
не будет), так как в стек "закладывается" полный адрес возврата (PCH + PCL).
Но при этом имеется одна особенность, незнание которой может привести к весьма
неприятным "непонимайкам".
Дело в том, что не следует "ставить знак равенства" между регистрами PCH и
PCLATH.
Они работают в "связке", и не более того. Это различные регистры.
В окне RАM (в области оперативной памяти ПИКа) отображается только регистр
PCLATH, а регистр PCH не отображается.
При возврате по стеку, между числовыми значениями этих регистров может возникнуть
"разнобой".
Он возникает в том случае, если имеет место быть возврат из того блока памяти
программ, который не является блоком, из которого произошел соответствующий
условный переход.
Например, условный переход на начало исполнения некой процедуры произошел из
1-го блока памяти программ (PCH = 00h) во 2-й (PCH = 01h).
Для того чтобы обеспечить "безбяковость" отработки процедуры, нужно записать в
регистр PCLATH число 01h.
В итоге, PCLATH = PCH = 01h.
Предположим, что процедура нормально отработана, после чего осуществляется
возврат по стеку.
9
Из вершины стека, в счетчик команд (PC), будет "загружен" (записан) полный адрес
возврата (PCH + PCL).
То есть, в PCH, "по верху" числа 01h ("гибнет"), будет записано число 00h.
Это обеспечивает "безбяковость" дальнейшей отработки программы.
Но в регистре PCLATH такого изменения не произойдет.
В нем так и останется "лежать" число 01h.
Получается то, что, в пределах 1-го блока памяти программ (PCH = 00h), происходит
нормальная работа, но с PCLATH = 01h.
Если этого не знать, то подобного рода "разнобой" может ввести в заблуждение.
А если знать, то можно определить, из какого именно блока памяти программ
произошел последний возврат (нет худа без добра).

Напоминание: возврат из ПП прерывания это тот же возврат по стеку, но только


являющийся следствием исполнения "виртуальной" команды call … (она в тексте
программы не "прописывается". Исполняется аппаратно).

Общее замечание: информация этого подраздела относится ко всем разновидностям


ПИКов среднего семейства (носит "базовый" характер).

"Практикум по конструированию устройств на PIC контроллерах"      http://ikarab.narod.ru       E-mail: karabea@lipetsk.ru

10