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

Память программ. Стек. Вычисляемые переходы.

Оглавление

Память программ
Общие положения
Карта пользовательской памяти программ, с обозначением области
конфигурирования
“Линейная” PC-адресация
Счётчик команд PC
Разделение пользовательской памяти программ на блоки и страницы
Страничные особенности работы
Аппаратный стек
Вычисляемые переходы
Стандартные ПП вычисляемых переходов
Универсальные ПП вычисляемых переходов
Применение, в таблицах вычисляемых переходов, директивы dt
Варианты программных процедур вычисляемых переходов, применяемых при
работе с ЖК-модулями (на основе м/к HD44780).
Стандартные и универсальные ПП вычисляемых переходов
Вариант универсальной подпрограммы вычисляемого перехода с
идентификатором конца таблицы
Варианты программных процедур вычисляемых переходов, применяемых при
работе с графическими модулями (на основе м/к KS0108).
Стандартные и универсальные ПП вычисляемых переходов
Вариант универсальной подпрограммы вычисляемого перехода с
идентификатором конца таблицы
Способы организации вычисляемых переходов в приложении к произвольному
порядку формирования “прыжковых” чисел (для графического модуля на основе м/к KS0108).
Принципы командноскоростной оптимизации процедур вычисляемых переходов
Использование вычисляемых переходов с целью выборов сценариев
дальнейшей работы программы

1
Пояснение: по причине того, что работа со стеком и с вычисляемыми переходами предполагает
“въезд в память программ”, то перво-наперво необходимо разобраться именно с ней.

В любом ПИКе имеются три функционально различные области памяти, которые


обеспечивают их (ПИКов) функционирование:

Обращения к этим видам памяти осуществляется по соответствующим внутренним,


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

Память программ
Общие положения
Общепринятая абревеатура памяти программ - PC
(так короче. На рис. 1 выделено светло-голубым цветом).
Память программ состоит из:
 пользовательской памяти программ,
 области конфигурирования.
Кроме того, память программ “укомплектована” (“на правах обязательной обслуги”)
 счётчиком команд PC,
 аппаратным стеком.
Пользовательская память программ
Это та область памяти программ, которая доступна пользователям (то есть, программистам /
конструкторам / любопытствующим /...).
Проще говоря, это то, что отображается в окне MPLAB с названием Program Memory.
Пользовательская память программ состоит из двух частей: активной и пассивной
(за исключением “стёртого ПИКа”. В этом случае есть только одна часть - пассивная) .

Активная часть пользовательской памяти программ это та её часть, в которой


“дислоцируются” коды команд программы.
В тексте программы, её начало помечается директивой org (это в конце “шапки” программы.
Активно то, что ниже её), а её конец помечается директивой end (активно то, что выше её).

Пассивная часть пользовательской памяти программ это та её часть, которая


не задействована.
Проще говоря, то, что ниже директивы end, в окне Program Memory, помечается
как “подряд идущие пустышки” типа addlw 0xff (в MPLAB версии 5.70.40) или 3FFF
(в современных версиях MPLAB).

Примечание: 3FFFh - адрес последней ячейки области конфигурирования.

Естественно, что в процессе работы над текстом программы, соотношение, между


этими частями, изменяется.

2
Область конфигурирования
Область конфигурирования “дислоцируется ниже нижней границы” пользовательской
памяти программ (адреса 2000h ... 3FFFh).
В ПИКах базового и среднего семейства, обращения к содержимому области
конфигурирования возможны только в режиме программирования ПИКа.

Примечание: в ПИКах 18-й серии гораздо симпатичнее, так как конструктор, по ходу исполнения программы, имеет
возможность изменять параметры конфигурации.

Итак, с основной терминологией разобрались (надеюсь на это), а теперь необходимо более


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

На рис. 2 изображена 4-столбцовая таблица, “привязанная” к самым распространённым


типам ПИКов среднего семейства.
Пока речь идёт о сравнении их PC-возможностей.
Самый левый столбец “посвящён” максимальным PC-возможностям (8 Килослов), а
далее по убывающей (4, 2, 1).

Примечание: 1 Килослово/Кбайт/Кбит = 1024 слова/байта/бита.

3
Как видите, по максимуму, PC-возможности реализованы в PIC16F876/876A,
PIC16F877/877A, а в других указанных типах ПИКов, они используются частично.
Обратите внимание на то, что в пользовательской памяти программ (она ограничена красной
рамкой) закрашено тёмно-серым цветом (с пометкой “Не используется”).
Это физически нереализованные области PC.
Проще говоря, там нет PC-ячеек, а значит и обращаться туда и не нужно (хотя и можно,
но об этом ниже), и бесполезно.
Физически реализованная область PC (“рабочая” область PC) состоит из определённого
количества PC-ячеек (в рассматриваемых случаях: 8192, 4096, 2048, 1024. Зависит от типа ПИКа).
Одна PC-ячейка предназначена для энергонезависимого запоминания 14 – битного кода
любой из команд, входящей в систему команд (см. “разрисовку” кодов команд: стр. 29-7 … 29-42
“Справочника по среднему семейству …”).

Примечание: 14-битный код, не являющийся кодом команды, исполняется как “пустой” машинный цикл. То есть,
результат исполнения этой “неправильной” команды такой же, как результат исполнения “правильной” команды NOP.
Это к вопросу о том, откуда возникают “виртуальные” NOPы.

Код программы это “набор” кодов команд, входящих в состав программы.


Когда дело доходит до “прошивки” (запись данных) ячеек пользовательской памяти
программ, то она начинается от PC-ячейки с адресом 0000h.
После осуществления этой записи (код команды с номером N записан в ячейку с адресом 0000h),
PC-адрес автоинкрементируется, после чего, в PC-ячейку с адресом 0001h,
производится запись кода следующей команды (с номером N+1).
Проще говоря, порядок записи à последовательный: за “один присест”, +1 к
предыдущему номеру команды и +1 к PC-адресу, после чего 14-битный код
соответствующей команды, с помощью программы, обслуживающей программатор,
записывается в соответствующую PC-ячейку.
И так далее, по направлению сверху вниз (по тексту программы), в порядке следования
команд.
По окончании “прошивки” ПИКа, в активной части пользовательской памяти программ
будет “лежать” энергонезависимо запомненный/сохранённый код программы, элементы
которого (элемент - код команды), в процессе исполнения программы, в большинстве случаев
(за исключением работы с Flash памятью программ), подлежат последовательному считыванию
(со всеми вытекающими).

В дальнейшем, “железяка” обязательно воспользуется “заложенными”, в код программы,


замыслами конструктора, но с одной оговоркой: “глобальный” порядок считывания
не единообразен, а разнообразен (!!!).
Он разнообразен потому, что замыслы конструктора могут быть всяческими, и в
первую очередь в части касающейся “железобетонного” (за исключением явного примитива)
применения “прыжковых” команд (это о командах call и goto, “рассредоточенных там-сям”).
Кроме того, многие программы содержат в себе “прыжковые” процедуры (это о прерываниях
и вычисляемых переходах).
Всё это привносит, в работу программы, заданную разработчиками, “адреснопрыжковую”
специфику (совсем не хилую), неучёт которой чреват различными неприятностями (в основном,
серьёзными и даже очень. Типа “вожделеется радость, а тут бац, и головоломное горе”) .

Разбираемся с этой спецификой (с сАмого начала).


Вопрос: “Что записано в активную часть PC на стадии “прошивки”?
Ответ: “набор” 14 – битных кодов команд программы.
То есть, “рабочее” содержимое PC в наличии, и по фактам обращений к этому
содержимому (в виде считываний), будут производиться какие-то действия, детали которых
оговорены в системе команд.
С учётом того, что программа исполняется последовательно / ”пошагово” и PC-ячеек
много, вне сомнений, без PC-адресации просто не обойтись, ведь здравый смысл
подсказывает, что сначала нужно адресно выбрать PC-ячейку, а всё остальное потом.
Вопрос: “Предположим, что нужная (условно) PC-ячейка выбрана. А дальше-то что”?
Ответ: адресный выбор PC-ячейки есть “отмашка” (“выстрел из стартового пистолета, пинок под зад”
и т.п.) на исполнение той 14-битной команды, которая “лежит” в этой ячейке.
Вопрос: “Как именно она исполняется”?
4
Ответ: путём считывания / копирования (из адресно выбранной PC-ячейки) кода команды, в
“могучие, аппаратные дебри” некоего сложного, исполнительного устройства, которое
“отвечает” за чёткое исполнение тех “правил игры”, которые “заложены” в коде текущей
команды (если толковать об их деталях, то это вопрос к разработчикам).
После исполнения предыдущей команды (любая команда исполняется за 4 такта Q1 ...Q4), происходит
адресное обращение к другой PC-ячейке (то есть, к следующей или к иной команде), и всё
повторяется, только в приложении к содержимому текущей PC-ячейки.
Вопрос: “По каким правилам происходят адресные выборы PC-ячеек”?
Ответ: по разным (не сочтите за наглый ответ. Он кОроток и правдив).
Разбираемся поэтапно.
“Линейная” PC-адресация
Это самое простое правило адресных выборов PC-ячеек.
Если часть программы не содержит в себе “прыжковых” команд call (естественно, в “связке” с
командами возвратов) и/или goto, прерывания запрещены и вычисляемых переходов нет, то
по ходу исполнения программы, PC-адрес просто последовательно инкрементируется.
Я это называю “линейной” частью программы (соответственно, программа исполняется “линейно”).
В этом случае, рабочая точка программы не “мечется как угорелая туда-сюда”, а
“степенно шагает по направлению сверху вниз” (по тексту программы. В том числе и с отработкой
“виртуальных” NOPов. Это о командах ветвлений) и конструктору не нужно предпринимать никаких
мер PC-адресных коррекций (“железяка всё сделает в лучшем виде”), а вот когда применяются
команды call и/или goto и/или используются вычисляемые переходы, то эта
“степенность” нарушается (чего только рабочая точка не вытворяет …), и конструктор, не всегда,
но частенько, поневоле “упирается” в необходимость осуществления PC-адресных
коррекций.
Для того чтобы с этим разобраться, нужно “въехать” в другие, более сложные детали
PC-адресации.

Примечание: В пиках 18-й серии, кроме “прыжковых” команд call, goto, есть ещё и другие “прыжковые” команды.

Счётчик команд PC
Адресными “PC-делами рулит” счётчик команд PC.
Выбор адреса PC-ячейки осуществляется с помощью управляющих регистров PCL
(младший) и PCH (старший).
Регистр PCL доступен для чтения/записи (является регистром спецназначения), а регистр PCH,
хотя и физически реализован, но не доступен для чтения/записи (не является регистром
спецназначения).
В части касающейся регистра PCL, необходимость “присвоения ему статуса” регистра
спецназначения, причём, отображаемого во всех банках, диктуется наличием такой
полезной “штуковины”, как вычисляемый переход (см. ниже), “атрибутом” которого
является программное приращение содержимого регистра PCL (если бы регистром PCL нельзя
было управлять, то и вычисляемых переходов не было бы).
А вот с регистром PCH немного “похитрее”.
Постараюсь объяснить.
Во многих случаях, воздействовать на его содержимое просто нет необходимости, но
есть два исключения:

 переходы, с одной страницы пользовательской памяти программ, на


другую (это для ПИКов с “объёмом” пользовательской PC более 2 Килослов),
 переходы, из одного блока страницы пользовательской памяти
программ, в другой (это “епархия” вычисляемых переходов).
Для того чтобы это можно было осуществить, разработчики придумали
регистр-посредник PCLATH.
Это регистр спецназначения типа “двойник регистра PCH”, но не его “полный клон”.
А именно:
1. В регистр PCLATH нельзя скопировать содержимое регистра PCH.
5
2. Косвенно скорректировать содержимое регистра PCH можно, так как запись в
регистр PCLATH означает запись в регистр PCH.
3. Прочитать содержимое регистра PCLATH можно, но результатом этого чтения
будет не содержимое регистра PCH (см. пункт 1), а ранее скорректированное
содержимое регистра PCLATH (см. пункт 2).

;*****************************************************************************************
; Доказательство утверждения, сформулированного в пункте 1.
;*****************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; Контрольный регистр.
endc ; Конец блока.
;------------------------------------
org 0 ; Обеспечение перехода
goto START ; в ПП START.
org 205h ; Смещение “нижележащего” в блок 2.
;****************************************************************************************

;----------------------------------------------------------------------------------------
START clrf Reg ; Reg=0
movf PCLATH,W ; Если бы PCH считывался в PCLATH, то было бы
movwf Reg ; Reg=2, но этого нет (Reg=0).
goto $ ; "Мертвяк".
;****************************************************************************************
end ; Конец программы.

В этой простейшей программе, работу которой можно проверить в симуляторе, ПП START, с помощью директивы
org 205h, смещена в блок 2, а переход в ПП START (goto START) осуществляется из блока 0 (см. org 0).
То есть, PCH = 0.
Далее происходит переход в блок 2, что соответствует PCH = 2, что и есть в реале (проконтролировать нельзя, так
как содержимое регистра PCH недоступно для чтения).
Если бы содержимое регистра PCH “клоноотображалось” в регистре PCLATH, то в дальнейшем, в регистр Reg
должно скопироваться число 2 (см. текст программы), но в конечном итоге, Reg = не 2, а 0.
Это можно проверить в окне Watch.

Примечание: в ПИКах 18-й серии, по причине наличия абсолютной адресации (это о call и goto), осуществляемой
в “границах” максимально возможного “объёма” PC, необходимость страничной коррекции “отпала” (то есть,
производить её не нужно), но необходимость блочной коррекции не “отпала”, и её суть такая же, как в ПИКах
базового и среднего семейства. Поэтому советую обратить самое пристальное внимание на то, о чём говорится в
этой статье (и особенно на “блочные дела”).

Вывод: функция регистра PCLATH - исключительно “странично-блочная”


коррекция содержимого регистра PCH (посредством записи в PCLATH), с возможностью
контроля ранее произведённой коррекции (чтение содержимого регистра PCLATH).
“Заморачиваться” регистром PCLATH нужно только в этих, специфических
случаях, а в остальных случаях (например, в случаях осуществления “линейной” PC-адресации), на
него можно “закрыть глаза”.

Это неизбежно приводит к мысли о целесообразности наведения, в этой “PC-епархии”,


элементарного, понятийного порядка типа “разложение по полочкам”.
А именно:
1. Если речь идёт о “въезде в странично-блочные дела”, то целесообразно
толковать не о “связке” PCH / PCL, а о “связке” PCLATH / PCL.
2. В остальных случаях, целесообразно толковать не о “связке” PCLATH / PCL, а о
“связке” PCH / PCL.
Прежде чем “въезжать в странично-блочные дела”, необходимо познакомиться с
общими положениями.

6
Разделение пользовательской памяти программ на блоки и страницы

Как видите, “битность” регистров PCL и PCH стандартна (по 8 битов), но в отличие от
регистра PCL, старшие 3 бита регистра PCH являются “пустышными” (нерабочими).
То есть, адресное слово состоит из 5 + 8 = 13-ти двоичных разрядов (битов), что, по
максимуму, позволяет адресоваться к любой ячейке 8-Килословного массива данных.
Но ведь в некоторых типах ПИКов используется только часть этого массива (см. рис. 2),
что, в зависимости от типа этих “мелкотравчатых” (условно) ПИКов, формально/условно,
если так можно выразиться, “выводит из строя” от 1-го до 3-х битов регистра PCH.
На рис. 3, стрЕлки этих битов имеют светло-серый цвет.
Судя по тому, что “поголовно”, во всех даташитах ПИКов среднего семейства (включая и
1-Килословные ПИКи), говорится именно о 13-разрядном/битном счётчике команд PC, он
имеется во всех типах этих ПИКов.
Пусть будет так, но в этом случае резонно предположить, что биты, помеченные
светло-серыми стрЕлками, ничем не управляют (нельзя управлять тем, чего нет).
То есть, в эти биты регистра PCH, с помощью регистра PCLATH, можно что-то
записать, но такое управляющее воздействие будет проигнорировано, если так можно
выразиться, “в пользу физически реализованного”.
Например, “слабосильный” (условно), 1 – Килословный ПИК PIC16F84A.
Для работы с 1 – Килословной пользовательской памятью программ, нужно не 13
двоичных разрядов, а 10.
В этом случае, кроме того, что игнорируются биты №7, №6, №5 регистра PCH
(это само собой разумеющееся), игнорируются ещё и управляющие действия битов
№4, №3, №2.

Примечание: состояния битов регистра PCL никогда не игнорируются.

Допустим, что после инициализации PIC16F84A, (PCH = PCLATH = xxx00000) и по ходу


исполнения программы, в “связке” PCH / PCL, установилось xxx00011 11110000, что не
выходит за пределы пользовательской памяти программ.
Если вместо 000 будет любая другая комбинация, то это, проще говоря, “по барабану”
(мотивация - см. выше), что соответствует адресному выбору той же PC-ячейки.
Если обозначить 000 как yyy, то для 1 – Килословных ПИКов форма этого адреса
будет такой: xxxyyy11 11110000 .
7
где xxxyyy - те биты, состояния которых “по барабану”:
 хх - по причине физической нереализованности,
 yyy - по причине незадействованности.
А если изобразить в общем виде и “собрать всё до кучи”, то:
 для 1 – Килословных ПИКов, xxxyyyNN NNNNNNNN
 для 2 – Килословных ПИКов, xxxyyNNN NNNNNNNN
 для 4 – Килословных ПИКов, xxxyNNNN NNNNNNNN
 для 8 – Килословных ПИКов, xxxNNNNN NNNNNNNN
Где N (для PCH) и N (для PCL) могут принимать значения 0 или 1.
Теперь, в целях более детального “расширения/углубления ранее нарытого”, вернусь к
сАмому простом “механизму” PC-адресации типа “последовательные приращения
PC-адреса на единицу”, который работает на “линейных” участках программы (см. выше).
В этом случае, речь идёт о банальном, 2-байтном регистре инкрементного счёта (PCH/PCL).
То есть, по факту отработки предыдущей команды, содержимое регистра PCL
аппаратно инкрементируется, что есть начало исполнения последующей команды.
И так далее, вплоть до смены содержимого регистра PCL, с FFh, на 00h, что есть
переход на начало следующего “витка” счёта, а “по совместительству”, аппаратный
перенос в регистр PCH (или аппаратный инкремент его содержимого) . И т.д.
Те люди, которые в курсе деталей работы программной процедуры N-байтного счёта
(в частности, самого простого, 2-байтного), легко поймут суть этого “механизма”.
Принципиальная разница только в том, что в одном случае, инкременты содержимого
младшего регистра (L) и переносы в старший регистр (H) организуются программно, а в
другом случае, они организуются аппаратно (по аналогии с работой 2-байтного регистра
TMR1H / TMR1L таймера TMR1, только TMR1H является регистром спецназначения, а PCH им не является).
Особенностью такого способа PC-адресации является то, что он работает
по всему массиву PC-ячеек пользовательской памяти программ, вплоть до
максимального, 8-Килословного массива, но в реале, “линейными” могут быть
только части программы, а не вся программа, так как последнее входит в вопиющее
противоречие с принципом цикличности её исполнения (цикличность связана с “прыгучестью”).
Если рассуждать “глобально”, то любую программу можно представить в виде
совокупности “линейно” исполняемых частей программы (по “массе”, в большинстве случаев, это
основная часть программы), разделённых, если так можно выразиться, “прыжковыми
вкраплениями”.
“Прыжковые вкрапления” это либо команды call и/или goto, либо они же, плюс
команды, подготавливающие эти call/goto – “прыжки”.
Теперь более детально разбираемся с только что сказанным.
Если речь зашла о “странично-блочных” делах, то рис. 3 целесообразно предоставить
в таком виде (без регистра PCH):

8
В приложении к 8-Килословной (то есть, по максимуму), пользовательской памяти программ
(PIC16F876/876A, PIC16F877/877A), разделение на страницы и блоки выглядит так:

Как видите, пользовательская память программ разделена на 2-Килобайтные страницы,


каждая из которых разделена на 256-словные блоки (намёк на регистр PCL).
Это разделение выглядит так:

Если речь идёт о менее “килобайтной”, пользовательской памяти программ, то


физически нереализованные её области, образно выражаясь, “выпадают в осадок”
(по большому счёту, на эти нерабочие “штуковины” можно не обращать внимания).
“Нижележащие” картинки нарисованы с учётом этого:

9
Даже поверхностно “просканировав” эти картинки, можно сделать вывод:
разделение на PC-страницы присуще только пользовательской памяти
программ “объёмом” более 2 Килослов. Если он менее 2 Килослов или равен
этому значению, то разделение на PC-страницы отсутствует.
Проще говоря, в последнем случае, разделением на PC-страницы “заморачиваться” не
нужно, хотя, формально, страница 0 (или её половина) есть.
Одна-разъединственная (а на рис. 7 вообще половинка).
А раз это так, то долой формализм.
Мотивация: как говорится, “один в поле не воин” и такого “воина” можно
проигнорировать (что и сделано на рис. 6 и рис. 7).
Соответственно, нужно учесть сказанное выше (для Вашего удобства, дублирую):
 для 1 – Килословных ПИКов, xxxyyyNN NNNNNNNN
 для 2 – Килословных ПИКов, xxxyyNNN NNNNNNNN
 для 4 – Килословных ПИКов, xxxyNNNN NNNNNNNN
 для 8 – Килословных ПИКов, xxxNNNNN NNNNNNNN

10
Только с поправкой: N (для PCLATH, а не для PCH) и N (для PCL. Это без поправки) могут
принимать значения 0 или 1.
Мотивация: скорректировать содержимое регистра PCH можно только посредством
регистра PCLATH.
В приложении к указанным типам ПИКов, разделение на PC-страницы имеется в
пользовательской памяти программ PIC16F876/876A, PIC16F877/877A (4 страницы) и
PIC16F873/873A, PIC16F874/874A (2 страницы).
Вопрос: “К какой стенке прислонить это разделение? Зачем оно нужно? Что с ним
делать”?
Ответ: разделение на PC-страницы следует учитывать только при работе с
“прыжковыми” командами call и goto, и не более того.

Страничные особенности работы

В ПИКах базового и среднего семейства, “прыжковые” команды call и goto


могут работать только в 11-разрядном (2048) адресном массиве.
Это и есть причина разделения на страницы.

А именно:

1. Если “объём” пользовательской памяти программ от 2 Килослов и менее,


то команды call и goto являются командами условного и безусловного,
абсолютного перехода.
В этом случае, команды call и goto работают в “границах” всей пользовательской
памяти программ, поэтому “PC-страничная PCLATH-коррекция” не нужна.

2. Если “объём” пользовательской памяти программ более 2 Килослов,


то команды call и goto являются командами условного и безусловного,
относительного перехода.
В этом случае, если не осуществлять “страничную PCLATH-коррекцию” (а значит и
коррекцию содержимого регистра PCH), команды call и goto работают не в “границах” всей
пользовательской памяти программ, а в “границах” той её PC-страницы, которая
текущезадана в регистре PCH.

Таким образом, в части касающейся пункта 1, имеет место быть “красота несусветная”
(пользовательская), а вот с пунктом 2 нужно разбираться.
Разбираемся.
Дано: например, 8-килословный ПИК PIC16F876, активная часть пользовательской
памяти программ которого “дислоцируется” во всех 4-х страницах.
Начну с того, что после осуществления инициализации ПИКа (сброс POR), равно как и
после осуществления любого другого вида сброса, по умолчанию:
PCH = PCLATH = xxx00000 и PCL = 00000000
Это означает то, что исполнение программы всегда начинается с PC-ячейки, имеющей
адрес 0000h (на рис. 2, она обозначена как Адрес (вектор) сброса. Во многих случаях это команда goto START, но
может быть и другая команда), с которой начинается страница 0, а также и то, что событие
любого вида сброса, произошедшее в “границах” любой PC-страницы, приведёт к тому
же (переход на Адрес (вектор) сброса. Это страница 0).
В данном случае (4 PC-страницы), выбором PC-страниц “рулят” биты регистра PCLATH с
№4 и №3 (напоминаю о том, что запись в регистр PCLATH это то же самое, что запись в регистр PCH).
То есть, если необходимо перейти из одной страницы в другую, то нужно
соответствующим образом скорректировать содержимое либо одного из этих битов,
либо обоих (это зависит от того, из какой именно, и в какую именно страницу осуществляется переход) .

11
Если необходимо сменить только PC-страницу, то при осуществлении
“предпрыжкового” выбора PC-страницы (по сути, это предустановка), другие биты
регистра PCLATH (№2 … №0) “трогать” не нужно (это биты выбора блоков), а иначе
переход будет ошибочным (попадёте на нужную страницу, но не в вожделеемый блок).
За исключением тех случаев, когда нужно сменить
и PC-страницу, и PC-блок.

Примечание: Если речь идёт о 2-страничном ПИКе, то выбором PC-страниц “рулит” один бит регистра PCLATH с №3
(а не два бита). Бит с №4 не задействован (на рис. 5 помечено как y).
Так как с регистром PCLATH можно работать в любом банке, то смена страниц не требует смены банков.
Пояснение: банки это “атрибут” другого вида памяти, а именно, оперативной памяти данных (см. рис. 1).

Допустим, что программа, в “PC-границах” страницы 0, “сделала свои дела” и по ходу


исполнения программы, нужно осуществить переход/“прыжок” на начало некой ПП,
“дислоцирующейся” в “PC-границах” страницы 3.
Значит, в соответствии с рис. 4:
; ....................
bsf PCLATH,3 ; Выбор
bsf PCLATH,4 ; страницы 3.
;-------------------------------
Вариант 1.
;-------------------------------
call <название ПП> ; “Прыгаем”.
; ----> Возврат из ПП.
; ....................
;-------------------------------
Вариант 2.
;-------------------------------
goto <название ПП> ; “Прыгаем”.
; ....................
Полагаю, что одного этого примера достаточно для того чтобы понять “сермяжную суть
межстраничных переходов”. А это детали:
Особенности Варианта 1.
Исполнение команды call сопровождается “закладкой”, в стек (см. ниже), адреса возврата.
Адрес возврата имеет абсолютную адресацию à работа не в 11-разрядном (2048),
а в 13-разрядном (8192) адресном массиве.
По этой причине, перед исполнением команды возврата, номер страницы
корректировать не нужно (хотя и можно, но это бесполезное/лишнее действие).
В рассматриваемом случае, возврат произойдёт на страницу 0, а если рассуждать в
общем виде, то возврат произойдёт на ту PC-страницу и в тот её блок (это о PCH), из
которого вызывалась ПП, причём, с числовым значением PC-адреса, увеличенным
(относительно числового значения PC-адреса команды call) на 1 (PCL + 1).
Практический вывод: “межстраничный” переход, с применением команды call,
требует программной корректировки содержимого битов №4 и №3
(для 8-Килословных ПИКов) или бита №3 (для 4-Килословных ПИКов) регистра PCLATH, а
возврат этой корректировки не требует.
Особенности Варианта 2.
Так как в этом случае стек не задействуется (работа только в 11-разрядном, адресном массиве), то
практический вывод таков: “межстраничный” переход, с применением команды
goto, во всех случаях требует программной корректировки содержимого битов
№4 и №3 (для 8-Килословных ПИКов) или бита №3 (для 4-Килословных ПИКов) регистра PCLATH.
Надеюсь на то, что прочитав/усвоив “вышележащую” информацию, Вы не будете
испытывать трудностей с “межстраничными” переходами, которые могут быть
востребованы при работе с 4/8-Килословными ПИКами (естественно, что это не относится к ПИКам,
“объём” PC которых не более 2 Килослов).

Теперь “на повестке дня разборки” с блоками, ведь разделение на блоки чем-то
мотивировано, а иначе зачем “городить этот блочный огород”?

12
Забегая вперёд, скажу, что разделение на блоки обусловлено особенностями
работы программных процедур с названием “вычисляемые переходы”, которые,
во многих случаях (это о “классических” вычисляемых переходах), если так можно выразиться,
являются “симбиозом блочного и стекового”, что, в свою очередь, предполагает
последовательный “въезд” в обе этих “составляющих”.
Начну со стека.

Аппаратный стек
В ПИКах базового и среднего семейства, стек это двунаправлено управляемое
(“загрузка/выгрузка”) “оперхранилище” 13-разрядных, то есть, абсолютных (работающих по
всему адресному массиву пользовательской PC) адресов возвратов.
Сие “оперхранилище”, хотя и не отображается в памяти программ и в памяти данных
(спасибо, что хоть симулятор выручает), но имеется в реале (просто “спрятался за угол”) и выглядит
примерно так (таблица стека выделена светло-голубым цветом):

13
Стек удобно изобразить (или представить себе) в виде таблицы (см. рис. 8).
Так как в ПИКах базового и среднего семейства, стек является 8-уровневым
(или 8-строчным), его таблица состоит из 8-ми строк.

Стек задействуется:
 если отрабатывается “связка”: call (программная “загрузка”) ... return/retlw (программная
“выгрузка”).
 если отрабатывается “связка”: уход в ПП прерывания (аппаратная “загрузка”) ...
retfie (программная “выгрузка”).

“Загрузка”, в таблицу стека, адреса возврата

Если исполняется команда call, то по окончании её исполнения, происходит


“штатный” вызов соответствующей подпрограммы, и автоинкрементированный (+1)
PC-адрес этой команды (то есть, PC-адрес следующей команды) “загружается” в вершину стека
(то есть, в самую верхнюю строку таблицы стека) .

Если во время исполнения какой-то команды произошло событие прерывания,


то “железяка” дожидается окончания исполнения этой “какой-то” команды (а иначе будет
“бардальеро”), после чего рабочая точка программы “прыгает” в PC-ячейку с адресом
0004h (на рис. 2 обозначено как Адрес (вектор) прерываний), и автоинкрементированный (+1)
PC-адрес этой “какой-то” команды (то есть, PC-адрес следующей команды), “загружается” в
вершину стека.

Если осуществляется работа с прерываниями, то в ПИКах базового и


среднего семейства, первая команда подпрограммы прерываний, в
обязательном порядке, должна “дислоцироваться” в PC-ячейке с адресом 0004h.
“Загрузка” текущего адреса возврата (он считывается из PCH / PCL, плюс автоинкремент), в вершину
стека, приводит к синхронному смещению содержимого таблицы стека на одну позицию
вниз.

Примечание: указанный выше, аппаратный автоинкремент PC-адреса необходим для того чтобы осуществить
дальнейший, адресный возврат не на ту команду, по окончании отработки которой произошёл вызов вызываемой ПП
или ПП прерывания (в этом случае, будет “жесточайший глюк”), а на следующую, после неё, команду. Этим
обеспечивается продолжение работы программы (после возврата) от этой команды.

“Выгрузка”, из таблицы стека, адреса возврата

Эта “выгрузка” (возврат) происходит по фактам исполнения команд возвратов.


Возврат, из вызываемой подпрограммы (вызов осуществляется с помощью команды call),
происходит по факту отработки команды возврата return или retlw.
Проще говоря, любая из команд условного перехода call должна быть
“укомплектована” командой возврата (return, retlw).
Это означает то, что в состав односценарной (в том смысле, что один сценарий возврата),
вызываемой ПП, в обязательном порядке, должна входить команда возврата, а в
состав многосценарной (в том смысле, что несколько сценариев возврата), вызываемой ПП,
должны входить несколько (по количеству сценариев возврата) команд возврата.
Несоблюдение этого правила (вызов есть, а возврата нет) приводит к “организационному
стек – бардальеро”, вплоть до переполнения стека (“глюк”).
Последовательность действий “железобетонно” должна быть такой:
вызов ПП à отработка ПП à возврат из ПП

Возврат, из подпрограммы прерываний (вызов осуществляется по факту возникновения


события прерывания, то есть, аппаратно), происходит по факту отработки команды возврата
retfie.
По причине “аппаратной природы реакции железяки” на событие прерывания

14
(в системе команд, нет команды ухода в ПП прерывания) ,
нужно озаботиться только возвратом из
ПП прерывания.
Это означает то, что в состав односценарной (в том смысле, что один сценарий возврата) ПП
прерывания, в обязательном порядке, должна входить команда возврата, а в состав
многосценарной (в том смысле, что несколько сценариев возврата) ПП прерывания, должны
входить несколько (по количеству сценариев возврата) команд возврата.
Несоблюдение этого правила, по сути, приводит к тому же “бардальеро” (см. выше).

“Выгрузка” текущего адреса возврата, из вершины стека, приводит к синхронному


смещению содержимого таблицы стека на одну позицию вверх.
Если “выгрузились” все адреса возвратов, то стек “очищается” и “ждёт” текущей
“загрузки” (в вершину стека).

Примечание: команды возвратов, если так можно выразиться, “символизируют собой” окончание отработки
односценарных / многосценарных (это о количестве сценариев возврата), вызываемых ПП или ПП прерывания.

Таким образом, “сермяжная суть” работы стека сводится к (если так можно выразиться)
“PC-адресному обеспечению двунаправленной прыгучести”, сначала “туда”, а потом
“сюда”, а в “промежутке”, между этими “туда” и “сюда”, исполняется какая-то нужная,
программная процедура (время её исполнения не лимитировано).
При таком “раскладе”, без “стек-обслуги” не обойтись.
А это “то же самое яйцо, но только в профиль”: по факту осуществления вызова
(вызываемой ПП или ПП прерывания), рабочая точка программы, дождавшись окончания
отработки текущей команды, на время “отлучается в вожделеемое место”, делает, в
этой “отлучке”, какие-то нужные (по замыслу конструктора) дела, а по окончании их “делания”,
возвращается на начало исполнения следующей (относительно той команды, на которую пришёлся
вызов) команды.
Теперь о крайностях.
Крайность №1
Если хотя бы одна команда call циклически исполняемой программы не
“укомплектована” командой возврата (return/retlw), то в конечном итоге произойдёт так
называемое “переполнение стека”, с дальнейшей его работой по кольцу.
То есть, начиная от 9-го адреса возврата и далее, по направлению сверху вниз
(относительно вершины стека), начнётся “смертоубийство” того, что ранее было “добыто тяжким
и непосильным стек-трудом” (запись “по верху”), со всеми вытекающими, “кладбищенскими”
последствиями (они просто отвратительны).
Если, в тексте ПП прерывания, конструктор забыл “настукать” команду retfie, то будет
то же самое.
Вывод: в любом случае, нельзя допускать переполнения стека.
Дело несколько осложняется тем, что отследить содержимое стека можно только в
симуляторе (в MPLAB версии 5.70.40 à окно Stack Window, в современных версиях версии à окно Hardvare Stack).
Проще, уяснив сказанное, не допускать переполнения стека, что, кстати, не так уж и
сложно (просто работа на внимание. Компетентность в этом вопросе - само собой разумеющееся).
Крайность №2
Если в тексте программы имеется лишняя (по причине ненужности/вредоносности), исполняемая
команда возврата, то она будет “паразитно” смещать содержимое стека вверх.
То есть, будут осуществляться преждевременные возвраты.
В итоге, это безобразие может закончиться (если ранее не “глюканёт”) тем, что официально
называется “исчерпанием стека” (а мне больше нравится слово “опустошение”), но только не нужно
путать это “исчерпание” (“бяка”) с другим “исчерпанием” (не “бяка”), которое, с целью
“разграничения полномочий”, целесообразно назвать “очищением” стека.
Если стек-ошибок нет, то неоднократное “очищение” стека - абсолютно нормальное,
“штатное” явление, которое обусловлено тем, что по ходу исполнения программы,
содержимое стека, по причине “загрузки/выгрузки” адресов возвратов (порядок
“загрузки/выгрузки” определяется замыслом программы) , смещается “туда-сюда”.

Примечание: в ПИКах базового и среднего семейства, нельзя программно изменить/прочитать содержимое стека,

15
поэтому он и назван аппаратным стеком.
В ПИКах 18-й серии, он, если так можно выразиться, “гибридный”, а в ПИКах 24-й серии, он программный.

На рис. 8, в общем виде, показан “механизм” исполнения того, о чём говорилось


выше.

Вычисляемые переходы
Стандартные ПП вычисляемых переходов
Вычисляемый переход это программная, “прыжковая” процедура
 считывания содержимого одной из строк таблицы вычисляемого
перехода (“классика”)
 или табличного выбора одного из сценариев дальнейшей работы
программы (и так тоже можно).
Это, как говорится, “за один присест”, но по ходу исполнения программы, таких
“присестов” может быть много, причём, не с абы какими, а с целенаправленными
обращениями к содержимому различных строк таблицы вычисляемого перехода.
“Железобетонным атрибутом” программной процедуры вычисляемого перехода является
команда addwf PCL,F (PCL + W = ... Результат операции сохраняется в регистре PCL), а ниже её (так как
речь идёт об увеличении числового значения PC-адреса) должна располагаться таблица вычисляемого
перехода (это тоже “атрибут”).

Примечание: если вместо addwf PCL,F написАть addwf PCL , то ошибки не будет, так как в том случае, если место
сохранения результата операции не указано, результат операции, по умолчанию, сохраняется в F (то есть, в
регистре, с содержимым которого производится операция). Это общее замечание, относящееся не только к
рассматриваемому случаю.

Таким образом, речь идёт о приращении (то есть, об увеличении) числового значения
младшего байта PC-адреса (регистр PCL) на величину числового значения содержимого
аккумулятора (W), что, по факту исполнения команды addwf PCL,F , неумолимо
приведёт к адресному “прыжку” рабочей точки программы в ту строку “нижележащей”
таблицы вычисляемого перехода (а значит и на соответствующую команду), “координаты” которой
“заложены” в регистре W.
Немаловажной и достаточно неудобной особенностью команды addwf PCL,F является
то, что она “никаким боком не прислонена” к регистру PCH (напоминаю о том, что
PC-адрес задаётся в “связке” PCH / PCL).
Она “прислонена” исключительно к регистру PCL, причём так, что в случае его
переполнения (переход от FFh к 00h), аппаратного переноса, в регистр PCH (PCH +1 = ...),
не будет, что чревато очень некрасивыми последствиями.

Примечание: вот Вам и причина разделения (причём, поневоле) пользовательской памяти программ на блоки
(см. рис. 4 … 7).

То есть, в этом “бякослучае”, “вычисляемопереходный, адресный прыжок”, через


“межблочную границу”, будет осуществлён не “туда, куда нужно”, а “туда, куда не
нужно”.

Примечание: поэтому в даташитах пишут (цитата): При выполнении вычисляемого перехода следует заботиться
о том, чтобы значение PCL не пересекло границу блока памяти (каждый блок 256 байт).
КЕА: не 256 байт, а 256 слов (между байтом и словом есть разница).

Допустим, что процедура вычисляемого перехода “дислоцируется на стыке” PC-блоков,


входящих в состав одной и той же PC-страницы.
Так как процедура “классического” вычисляемого перехода вызывается командой call,
которая, в “границах” PC-страницы, является командой абсолютной адресации, то
сначала будет норма, но только до смены содержимого регистра PCL, с FFh, на 00h.

16
Если не предпринимать мер по выбору соответствующего блока, то в этом случае,
будет адресно выбрана не верхняя ячейка “нижележащего” блока, а ячейка
“вышележащего” блока, с тем адресом (внутри этого блока), который указан в регистре PCL.
Соответственно, можно сформулировать правило:

Если никаких упреждающих мер не предпринято (о них ниже), то процедура


вычисляемого перехода должна “дислоцироваться” в одном PC-блоке (в любом,
с учётом “нижележащих, блочнокорректирующих” оговорок) и не выходить за его “границы”
(“межблочная граница” не должна проходить через таблицу вычисляемого
перехода).

Ещё одно правило:

“На влёте” в процедуру вычисляемого перехода, содержимое аккумулятора (W)


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

Теперь ближе к практике.


Сначала “классика” à “безбяковый” вычисляемый переход, “заточенный” под работу с 7-
сегментным индикатором (работу этой программы можно проверить в симуляторе и/или в “железе”):

Программа №1 (для 7-сегментного индикатора)


;***************************************************************************************************
; Программа, позволяющая проверить работу вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; Регистр перебора чисел.
;;; Reg_1 ; Времязадающие регистры
;;; Reg_2 ; фиксированной
;;; Reg_3 ; задержки.
endc ; Конец блока.
;------------------------------------
org 0 ; Первая команда программы “дислоцируется”
; в PC-ячейке с адресом 0000h.
goto START ; Переход в ПП START.
;***********************************************************************************************

;-----------------------------------------------------------------------------------------------
; Если есть желание проверить работу этой программы не только в симуляторе, но и в “железе”,
; то разблокируйте выделенное тёмно-зелёным цветом. Это то, что относится к фиксированной
; задержке. Если её не будет, то цифры, выводимые на индикацию, будут сменяться очень быстро.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Вызываемая ПП преобразования двоично-десятичного кода в код 7-сегментного индикатора.
; В данном случае, индикатор подключен к выводам порта В (запятая не задействуется).
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Для 7-сегментного индикатора С ОБЩИМ КАТОДОМ.
;=================================================
; “Загоранию” сегмента соответствует 1.
;-------------------------------------------------
TABLE addwf PCL,F ; PCL + W = ...
retlw b'00111111' ; ..FEDCBA -> символ “0” 3Fh
retlw b'00000110' ; .....CB. -> символ “1” 06h
retlw b'01011011' ; .G.ED.BA -> символ “2” 5Bh
retlw b'01001111' ; .G..DCBA -> символ “3” 4Fh
retlw b'01100110' ; .GF..CB. -> символ “4” 66h
retlw b'01101101' ; .GF.DC.A -> символ “5” 6Dh
retlw b'01111101' ; .GFEDC.A -> символ “6” 7Dh
retlw b'00000111' ; .....CBA -> символ “7” 07h
retlw b'01111111' ; .GFEDCBA -> символ “8” 7Fh
retlw b'01101111' ; .GF.DCBA -> символ “9” 6Fh

17
;==================================================
; Для 7-сегментного индикатора С ОБЩИМ АНОДОМ.
;==================================================
; “Загоранию” сегмента соответствует 0.
;-------------------------------------------------
;;;TABLE addwf PCL,F ; PCL + W = ...
;;; retlw b'11000000' ; ..FEDCBA -> символ “0” 0C0h
;;; retlw b'11111001' ; .....CB. -> символ “1” 0F9h
;;; retlw b'10100100' ; .G.ED.BA -> символ “2” 0A4h
;;; retlw b'10110000' ; .G..DCBA -> символ “3” 0B0h
;;; retlw b'10011001' ; .GF..CB. -> символ “4” 99h
;;; retlw b'10010010' ; .GF.DC.A -> символ “5” 92h
;;; retlw b'10000010' ; .GFEDC.A -> символ “6” 82h
;;; retlw b'11111000' ; .....CBA -> символ “7” 0F8h
;;; retlw b'10000000' ; .GFEDCBA -> символ “8” 80h
;;; retlw b'10010000' ; .GF.DCBA -> символ “9” 90h
; ...................................
;********************************************************************************************
; Начало программы.
;********************************************************************************************
START bsf STATUS,RP0 ; Так как TRISB “лежит” в 1-м банке, то переход в него.
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Так как PORTB “лежит” в 0-м банке, то переход в него.
;---------------------------------------------------------------------------------------
; Имитация “добывания прыжковых” данных (это “добывание” может быть всяческим).
; В данном случае, осуществляется простенький, последовательный перебор чисел от 0 до 9
; (и по-новой. Пока терпения хватит).
; Числу 0 соответствует символ “0”, 1 -> “1”, 2 -> “2” ... 9 -> “9”.
;---------------------------------------------------------------------------------------
SNOVA clrf Reg ; Перебор 10-ти значений (0..9) начинаем от ноля (Reg=0).
goto OBHOD ; Так как начинаем от ноля,
; то обход инкремента.
INKREMENT incf Reg,F ; Reg + 1 = ... Результат -> Reg.
movf Reg,W ; Reg -> W.
sublw .10 ; .10 – W(т.е. Reg) = ...
btfsc STATUS,Z ; В Reg “лежит” .10 или меньшее число ?
goto SNOVA ; Если .10, то снова, от ноля, перебираем
; 10 значений (0..9).
OBHOD movf Reg,W ; Если менее .10, то “прыжковое” число копируется в W.
call TABLE ; Вызов ПП TABLE.
; --> Возврат по стеку из ПП TABLE (“добытое лежит” в W).

movwf PORTB ; Вывод цифры на индикацию.


;---------------------------------------
; Фиксированная задержка на 1 сек.
;---------------------------------------
;;; movlw .173 ;
;;; movwf Reg_1 ; Запись
;;; movlw .19 ; времязадающих
;;; movwf Reg_2 ; констант.
;;; movlw .6 ;
;;; movwf Reg_3 ;

;;; decfsz Reg_1,F ;


;;; goto $-1 ; Стандартная
;;; decfsz Reg_2,F ; 3-байтная
;;; goto $-3 ; задержка.
;;; decfsz Reg_3,F ;
;;; goto $-5 ;
;---------------------------------------
goto INKREMENT ; Продолжаем перебор далее.
; ...................................
;***********************************************************************************************
end ; Конец программы.

А это для тех, кто пожелает проверить работу этой программы в “железе”:

18
В этом случае, целесообразно начать ПП START (после инициализации ПИКа, устанавливается 0-й
банк) с предустановки “защёлок” порта В в состояние типа “все сегменты погашены”.
Для индикатора с общим катодом, это clrf PORTB.
Для индикатора с общим анодом, это сначала movlw .255, а потом movwf PORTB.
То, что выделено тёмно-зелёным цветом, нужно разблокировать.
Программа написана под индикатор с общим катодом.
Если необходимо перейти на индикатор с общим анодом, то нужно разблокировать
нижнюю ПП TABLE и заблокировать верхнюю.
После включения питания, на индикаторе будут последовательно “высвечиваться”
символы цифр от 0 до 9 (смена цифр - через каждую секунду).
Потом 9 сменится на 0 и по-новой. И т.д. Пока не выключите питание.
Если предполагается проверить работу программы в симуляторе (без “железа”), то уберите
из текста программы всё то, что выделено тёмно-зелёным цветом.
Работу программы можно отследить в окне Watch, с “загруженным” в него регистром
PORTB.
После осуществления ассемблирования, щёлкните по иконке Animate (это о версиях MPLAB
8.хх. У меня установлена версия 8.10) и понаблюдайте за автоматической сменой числовых
значений содержимого регистра PORTB. Она должна происходить в таком порядке:
3F à 06 à 5B à 4F à 66 à 6D à 7D à 07 à 7F à 6F (конец одного полного цикла) à 3F à 06 à … (и так далее).
Если Вы разблокируете нижнюю ПП TABLE и заблокируете верхнюю, то будет то же
самое, но с другими числовыми значениями (они указаны в тексте программы).
О приятном сказал.
Теперь скажу о неприятном (в жизнеутверждающих/познавательных/упреждающих целях).
Для того чтобы разобраться в неприятном (зачем быть битым, если этого можно избежать?),
а заодно и сравнить его с приятным, “сваял” эту учебно-тренировочную программу:

Программа №2 (для 7-сегментного индикатора)


;***************************************************************************************************
; Программа, позволяющая проверить работу вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; Регистр перебора чисел.
endc ; Конец блока.
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; ВНИМАНИЕ! ПРИ ПРОВЕРКЕ, ДОЛЖНА РАБОТАТЬ ТОЛЬКО ОДНА ИЗ “НИЖЕЛЕЖАЩИХ” ДИРЕКТИВ org ...
; ОСТАЛЬНЫЕ ДОЛЖНЫ БЫТЬ ЗАБЛОКИРОВАНЫ + ВЫПОЛНЕНИЕ “НИЖЕЛЕЖАЩЕЙ” ...!!!! ИНСТРУКЦИИ !!!...
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; Варианты “безбяковой” работы.
; Можно назначить и другие PC-адреса, лишь-бы не выйти за “границы” блока.
;-----------------------------------------------------------------------------------------------
;;; org 0 ; Первая команда программы “дислоцируется” в блоке 0.
;;; org 100h ; Первая команда программы “дислоцируется” в блоке 1.
;;; org 200h ; Первая команда программы “дислоцируется” в блоке 2.
;;; org 300h ; Первая команда программы “дислоцируется” в блоке 3.
;-----------------------------------------------------------------------------------------------

19
; Варианты “бяковой” работы: “межблочная граница” проходит через таблицу вычисляемого перехода
; (см. окно Program Memory). В данном случае (для любого из вариантов), она проходит через
; середину таблицы вычисляемого перехода, но можно адресно сместить её выше или ниже.
;-----------------------------------------------------------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 0-го и 1-го блоков.
;---------------------------------------------
org 0F9h ; Текст программы адресно смещён так, что верхняя часть ПП
; TABLE “лежит” в блоке 0, а нижняя её часть, в блоке 1.
;---------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 1-го и 2-го блоков.
;---------------------------------------------
;;; org 1F9h ; Текст программы адресно смещён так, что верхняя часть ПП
;;; ; TABLE “лежит” в блоке 1, а нижняя её часть, в блоке 2.
;---------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 2-го и 3-го блоков.
;---------------------------------------------
;;; org 2F9h ; Текст программы PC-смещён так, что верхняя часть ПП
;;; ; TABLE “лежит” в блоке 2, а нижняя её часть, в блоке 3.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

; ................................ ; Часть программы (кроме случая org 0).


;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Вызываемая ПП преобразования двоично-десятичного кода в код 7-сегментного индикатора.
; В данном случае, индикатор подключен к выводам порта В (запятая не задействуется).
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Для 7-сегментного индикатора С ОБЩИМ КАТОДОМ.
;=================================================
; “Загоранию” сегмента соответствует 1.
;-------------------------------------------------
TABLE addwf PCL,F ; PCL + W = ...
retlw b'00111111' ; ..FEDCBA -> символ “0” 3Fh
retlw b'00000110' ; .....CB. -> символ “1” 06h
retlw b'01011011' ; .G.ED.BA -> символ “2” 5Bh
retlw b'01001111' ; .G..DCBA -> символ “3” 4Fh
retlw b'01100110' ; .GF..CB. -> символ “4” 66h
retlw b'01101101' ; .GF.DC.A -> символ “5” 6Dh <-- ТОЧКА ОСТАНОВКИ (для “бяк”)
retlw b'01111101' ; .GFEDC.A -> символ “6” 7Dh
retlw b'00000111' ; .....CBA -> символ “7” 07h
retlw b'01111111' ; .GFEDCBA -> символ “8” 7Fh
retlw b'01101111' ; .GF.DCBA -> символ “9” 6Fh
; ...................................
;********************************************************************************************
; Начало программы.
;********************************************************************************************
START bsf STATUS,RP0 ; Переход в 1-й банк. <-- ТОЧКА ОСТАНОВКИ
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Переход в 0-й банк.
;---------------------------------------------------------------------------------------
; Имитация “добывания прыжковых” данных (это “добывание” может быть всяческим),
; с программным ограничением “диапазона прыжка”.
;---------------------------------------------------------------------------------------
SNOVA clrf Reg ; Перебор 10-ти значений (0..9) начинаем от ноля (Reg=0).
goto OBHOD ; Так как начинаем от ноля,
; то обход инкремента.
INKREMENT incf Reg,F ; Reg + 1 = ... Результат -> Reg.
movf Reg,W ; Reg -> W.
sublw .10 ; .10 – W(т.е. Reg) = ...
btfsc STATUS,Z ; В Reg “лежит” .10 или меньшее число ?
goto SNOVA ; Если .10, то снова, от ноля, перебираем 10 значений (0..9).
; Если менее .10, то далее.
OBHOD
;===================================================================================================
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИНСТРУКЦИЯ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;===================================================================================================
; Если разблокировано org 0 или org 0F9h, то заблокировать всё то, что ниже выделено красным цветом.
;---------------------------------------------------------------------------------------------------
; Разблокировать, если разблокировано org 100h или org 1F9h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 1 ; Так как 1-я команда ПП TABLE “лежит”
;;; movwf PCLATH ; в блоке 1, то выбор блока 1.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 200h или org 2F9h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 2 ; Так как 1-я команда ПП TABLE “лежит”
;;; movwf PCLATH ; в блоке 2, то выбор блока 2.

20
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 300h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 3 ; Так как 1-я команда ПП TABLE “лежит”
;;; movwf PCLATH ; в блоке 3, то выбор блока 3.
;-----------------------------------------------------------
movf Reg,W ; “Прыжковое” число копируется в W.
call TABLE ; Вызов ПП TABLE.
; --> Возврат по стеку из ПП TABLE (“добытое лежит” в W).
movwf PORTB ; Вывод цифры на индикацию.
goto INKREMENT ; Продолжаем перебор далее.
;-----------------------------------------------------------
; ................................ ; Символизирует
; ................................ : часть программы.
;***************************************************************************************************
end ; Конец программы.

В этой программе, таблица вычисляемого перехода “дислоцируется на стыке” между


блоком 0 и блоком 1, но, изменив программные блокировки, можно выбрать любой из
7-ми предлагаемых вариантов (а если изменить адрес, то можно сместить “межблочную границу” вверх/вниз).
Группа “безбяковых” вариантов à 4 варианта работы внутри разных PC-блоков (0, 1, 2, 3).
Группа “бяковых” вариантов à 3 варианта “работы на стыках” PC-блоков (0/1, 1/2, 2/3).
Советую начать проверку с “безбяковых” случаев.
Тут всё просто.
В любом из этих случаев, порядок смены содержимого регистра PORTB:
3F à 06 à 5B à 4F à 66 à 6D à 7D à 07 à 7F à 6F (конец одного полного цикла) à 3F à 06 à … (и так далее.
Проблем нет). Далее, в симуляторе, “просканируйте бяковые варианты”.
В любом из них, проблемы обязательно будут, но не сразу, а после попытки
осуществления смены 66h на 6Dh (в данном случае), что, без осуществления “спецмер”,
является заранее обречённой на неудачу попыткой преодоления “межблочной границы”
(или перехода, из “вышележащего” PC- блока, в “нижележащий”).
Исполнение программы начинается так:

“Глюк”, возникающий при смене 66h на 6Dh, а заодно и “глюк” MPLAB, выглядит так:

21
Сначала о глюке MPLAB.
На рис. 10 Вы видите работу MPLAB версии 8.10: рабочая точка программы
“глюкопрыгнула” в ячейку с PC-адресом 063h.
Если сделать то же самое в MPLAB версии 5.70.40, то рабочая точка программы
“глюкопрыгнет” в ячейку с PC-адресом 032h.
Один только этот “разнобой” неумолимо наводит на мысль о том, что в приложении к
рассматриваемым случаям, “номиналы” PC-адресов этих “глюкопрыжков” не вызывают
доверия, а заодно и вводят в заблуждение (“с какого бодуна” 063h или 032h?).
Мотивация (с привлечением здравого смысла).
Перед осуществлением “перехода межблочной границы”, то есть, перед
осуществлением (в данном случае) попытки смены 66h на 6Dh, PCL = FAh (.250), а в W
“лежит” число .5 (это можно проверить в симуляторе).
addwf PCL,F à .250 + .5 + один автоинкремент PC-адреса = .256.
В итоге, PCL = .0, плюс аппаратный перенос в PCH, который, в данном случае, не
осуществляется.
Таким образом, действительности соответствует это:

1. Если верхняя часть таблицы вычисляемого перехода “дислоцируется” в блоке 0,


а нижняя часть, в блоке 1, то рабочая точка программы “глюкопрыгнет” в самую
верхнюю PC-ячейку блока 0, имеющую PC-адрес 0000h (так как в данном случае, после
инициализации ПИКа, по-умолчанию PCLATH = .0. Не путать со сбросом).

2. Если верхняя часть таблицы вычисляемого перехода “дислоцируется” в блоке 1,


а нижняя часть, в блоке 2, то рабочая точка программы “глюкопрыгнет” в самую
верхнюю PC-ячейку блока 1, имеющую PC-адрес 0100h (так как предустановка PCLATH = .1).

3. Если верхняя часть таблицы вычисляемого перехода “дислоцируется” в блоке 2,


а нижняя часть, в блоке 3, то рабочая точка программы “глюкопрыгнет” в самую
верхнюю PC-ячейку блока 2, имеющую PC-адрес 0200h (так как предустановка PCLATH = .2).

Примечание: как видите, “правильные” адреса “глюкопрыжков” 0000h, 0100h, 0200h отличаются от “неправильных”
адресов “глюкопрыжков” 0032h, 0132h, 0232h (для MPLAB версии 5.70.40) или 0063h, 0163h, 0263h (для MPLAB
версии 8.10).

Теперь, с учётом сказанного, непосредственно о “вычисляемопереходном глюке”.


Например, верхняя часть таблицы вычисляемого перехода “дислоцируется” в блоке 0,

22
а нижняя часть, в блоке 1, и рабочая точка программы “глюкопрыгнула” в начало
блока 0.
Это означает то, что вместо симпатичной, плановой реализации задумки конструктора,
“паразитнопринудительно” начнётся программное “оргбардальеро”, последствия которого
могут быть всяческими (это зависит от конкретики “паразитнопринудительно” исполняемой части программы).
Но всё это “мелочь пузатая” по сравнению со “стек-бардальеро”.
Дело в том, что перед “глюком”, в вершину стека “загружается” адрес возврата (так как
call TABLE), а соответствующая команда возврата (в данном случае)

retlw b'01101101' ; .GF.DC.A -> символ “5” 6Dh

не исполняется.
Получается, что команда вызова, по “паразитнопринудительным” причинам, оказалась
“неукомплектованной” командой возврата.
Если этот “глюк” повторится несколько раз, то произойдёт переполнение стека
(в симуляторе, можно увидеть процесс заполнения таблицы стека, а как “апофеоз” à “стек-предупреждалка”).
С учётом того, что эти “бяки работают в комплексе”, в итоге, речь идёт о
стопроцентном, жесточайшем, функциональном “глюке”, о который, если быть
некомпетентным в этих вопросах, частенько “разбиваются душ прекрасные порывы”.
Надеюсь на то (говорю на полном серьёзе), что “въехав в сермяжную суть этого типа
глючности”, техническая составляющая Ваших душ локально возрадуется (со всеми
вытекающими).
Голос из-за кулис: “Ну и настращал … А процедура-то компактная. В этом смысле,
красатулька/персик/... Как выкрутиться-то, Склифосовский”?
Ответ: привет, дружище! Рад пообщаться. Давно тебя не было. Если объявился,
значит я на верном пути.
На счёт “красатульки/персика/...” никто и не спорит.
Так оно и есть, но этот “персик” должен “дислоцироваться” строго внутри PC-блока и
не “выходить за его границы”, а иначе это будет совсем не “персик”, а обидный
“PC-кукиш”.
Лично я, предпочитаю компактно “валить эти персики” в блок 0, в виде так
называемой (мной) “верхней обслуги”.
Вопрос: “Почему так”?
Ответ: потому, что в этом случае, в процессе создания текста программы, сии
“персики”, в PC, не смещаются вниз, то есть, не изменяют свою “блочность”, а
“дислоцируются” строго в блоке 0.
Если имеется ПП прерывания (она “дислоцируется” почти в начале блока 0), то “персики”
целесообразно разместить ниже её.
Во многих случаях, ПП прерывания не очень массивна” и занимает только часть
блока 0, что позволяет разместить в нём и вызываемые ПП вычисляемых переходов.

Примечание: в этом случае, в идеале, текст ПП прерывания нужно “орготработать” в первую очередь. В том смысле,
чтобы в дальнейшем не изменять количество её команд, а иначе это приведёт к PC-смещению всего “нижележащего”
текста программы (возникает риск прохождения “межблочной границы” через таблицу вычисляемого перехода).
Если же без этого не обойтись (в жизни всякое бывает), то после осуществления любого такого изменения,
необходимо убедиться (в окне Program Memory) в том, что таблицы “нижележащих” ПП вычисляемых переходов не
“наехали” на “межблочные границы”, а если “наехали”, то необходимо, тем или иным способом (сместить,
“передислоцировать, из пункта А, в пункт Б” и т.п.), устранить эту “бяку”.

Если, в блоке 0, не получается разместить вызываемые ПП вычисляемых переходов


(например, в наличии имеется “массивная” ПП прерывания или ещё что-то/как-то) , то блок 1 (или с другим
номером) ничем не хуже, но только при условии того, что в ходе “ваяния” текста
программы, изменения “вышележащего” таковы, что они не приводят к
“вычисляемопереходному глюку” (контроль обязателен), а если приводят, то необходимо
предпринять программные меры по устранению этого оргконфуза.
Универсальные ПП вычисляемых переходов
Вопрос: “А можно сделать так, чтобы ПП вычисляемого перехода безбяково работала
даже тогда, когда межблочная граница проходит через его таблицу”?
Ответ: можно. В обмен на усложнение этой ПП.

23
В этом случае, речь идёт об универсальной ПП вычисляемого перехода, которая, если
так можно выразиться, “не боится межблочных границ”, а заодно и любых
“PC-смещений вышележащего”.
То есть, она может “дислоцироваться” в том “месте” текста программы, в которое её
сочтёт нужным поместить конструктор.
Прежде чем толковать о такой разновидности ПП вычисляемого перехода, нужно
разобраться с работой операторов high и low (есть ещё и upper, но в ПИКах базового и среднего
семейства этот оператор не востребован).
Вы знакомы (надеюсь на это) с оператором $.
Операторы high и low тоже “из этой епархии” (см. список арифметических операций, “Руководство
пользователя MPASM”, стр. 51), только со своими, специфическими функциями.
Проще всего разобраться на конкретном примере.
В приложении к “бяковым” вариантам, по сути (без учёта деталей), “нижележащая”
программа является как бы “двойником вышележащей” программы, но с одной
существенной/симпатичной оговоркой: в этих неблагоприятных условиях, программа не
“глючит” (как предыдущая), а нормально работает.

Примечание: “безбяковые” варианты “расписывать” не стал, но если у Вас есть желание убедиться в их
“безглючности”, то по аналогии с “вышележащей” программой, “врежьте” в текст “нижележащей” программы
;;; org 0 ; Первая команда программы “дислоцируется” в блоке 0.
;;; org 100h ; Первая команда программы “дислоцируется” в блоке 1.
;;; org 200h ; Первая команда программы “дислоцируется” в блоке 2.
;;; org 300h ; Первая команда программы “дислоцируется” в блоке 3.
и “поиграйте” с блокировками.

Программа №3 (для 7-сегментного индикатора)


;***************************************************************************************************
; Программа, позволяющая проверить работу ПП универсального вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; Регистр перебора чисел.
endc ; Конец блока.
;-----------------------------------------------------------------------------------------------
; “Межблочная граница” проходит через таблицу вычисляемого перехода (см. окно Program Memory).
; В данном случае (для любого из вариантов), она проходит через середину таблицы вычисляемого
; перехода, но можно адресно сместить её выше или ниже. При проверке, должна работать только одна из
; “нижележащих” директив org ... , а остальные должны быть заблокированы.
;-----------------------------------------------------------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 0-го и 1-го блоков.
;---------------------------------------------
org 0F2h ; Текст программы адресно смещён так, что верхняя часть ПП
; TABLE “лежит” в блоке 0, а нижняя её часть, в блоке 1.
;---------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 1-го и 2-го блоков.
;---------------------------------------------
;;; org 1F2h ; Текст программы адресно смещён так, что верхняя часть ПП
;;; ; TABLE “лежит” в блоке 1, а нижняя её часть, в блоке 2.
;---------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 2-го и 3-го блоков.
;---------------------------------------------
;;; org 2F2h ; Текст программы PC-смещён так, что верхняя часть ПП
;;; ; TABLE “лежит” в блоке 2, а нижняя её часть, в блоке 3.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

; ................................ ; Символизирует ПП прерывания (если она есть).

;###############################################################################################
; НАЧАЛО ВЕРХНЕЙ “ОБСЛУГИ” (вариант организации “обслуги”).
;###############################################################################################
; Вызываемая ПП преобразования двоично-десятичного кода в код 7-сегментного индикатора.

24
; В данном случае, индикатор подключен к выводам порта В (запятая не задействуется).
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Для 7-сегментного индикатора С ОБЩИМ КАТОДОМ. “Загоранию” сегмента соответствует 1.
;------------------------------------------------------------------------------------
TABLE movlw high PC_ADR ; Установка (через W), в PCH (посредством PCLATH), числового
movwf PCLATH ; значения старшего байта адреса той команды, которая помечена
; меткой PC_ADR.
;---------------------------------------------------
; Будем “прыгать” в текущий PC-блок или в следующий?
;---------------------------------------------------
movf Reg,W ; “Прыжковое” число копируется в W.
addlw low PC_ADR ; Суммирование "прыжкового" числа и числового значения младшего
; байта адреса той команды, которая помечена меткой PC_ADR.
btfsc STATUS,C ; Перенос был или нет?
incf PCLATH,F ; Если перенос был (соответствует работе в следующем PC-блоке),
; то PCLATH+1=... (то есть, PCH+1=...).
; Если переноса не было (соответствует работе в текущем
; PC-блоке), то команда инкремента не исполняется.
movf Reg,W ; “Прыжковое” число копируется в W.
;------------------------------------
; Таблица вычисляемого перехода.
;------------------------------------
addwf PCL,F ; PCL + W = ...
PC_ADR retlw b'00111111' ; ..FEDCBA -> символ “0” 3Fh
retlw b'00000110' ; .....CB. -> символ “1” 06h
retlw b'01011011' ; .G.ED.BA -> символ “2” 5Bh
retlw b'01001111' ; .G..DCBA -> символ “3” 4Fh
retlw b'01100110' ; .GF..CB. -> символ “4” 66h
retlw b'01101101' ; .GF.DC.A -> символ “5” 6Dh
retlw b'01111101' ; .GFEDC.A -> символ “6” 7Dh
retlw b'00000111' ; .....CBA -> символ “7” 07h
retlw b'01111111' ; .GFEDCBA -> символ “8” 7Fh
retlw b'01101111' ; .GF.DCBA -> символ “9” 6Fh
;-----------------------------------------------------------------------------------------------
; ................................ ; Другие вызываемые ПП
; ................................ ; верхней “обслуги”.
;###############################################################################################
; КОНЕЦ ВЕРХНЕЙ “ОБСЛУГИ”.
;###############################################################################################
; ................................ ; Символизирует
; ................................ : часть программы.
;********************************************************************************************
; Начало программы.
;********************************************************************************************
START bsf STATUS,RP0 ; Переход в 1-й банк. <-- ТОЧКА ОСТАНОВКИ
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Переход в 0-й банк.
;---------------------------------------------------------------------------------------
; Имитация “добывания прыжковых” данных (это “добывание” может быть всяческим),
; с программным ограничением “диапазона прыжка”.
;---------------------------------------------------------------------------------------
SNOVA clrf Reg ; Перебор 10-ти значений (0..9) начинаем от ноля (Reg=0).
goto OBHOD ; Так как начинаем от ноля,
; то обход инкремента.
INKREMENT incf Reg,F ; Reg + 1 = ... Результат -> Reg.
movf Reg,W ; Reg -> W.
sublw .10 ; .10 – W(т.е. Reg) = ...
btfsc STATUS,Z ; В Reg “лежит” .10 или меньшее число ?
goto SNOVA ; Если .10, то снова, от ноля, перебираем 10 значений (0..9).
OBHOD call TABLE ; Если менее .10, то вызов ПП TABLE.
; --> Возврат по стеку из ПП TABLE (“добытое лежит” в W).
movwf PORTB ; Вывод цифры на индикацию.
goto INKREMENT ; Продолжаем перебор далее.
;---------------------------------------------------------------------------------------
; ................................ ; Символизирует
; ................................ : часть программы.
;***************************************************************************************************
end ; Конец программы.

Операторы это не соответствующие “штатные” команды (намёк на систему команд), а то, к


чему они могут обратиться.
Обращение “штатной” команды, к оператору, вызывает специфическое, аппаратное
действие, результат которого есть “гибрид” логики команды и логики того оператора, к
которому команда обращается.
К операторам high и low можно обратиться с помощью любой из команд, входящих в
группу команд, работающих с константами.

25
Все эти команды являются байт-ориентированными, и любая из них работает с
константой и с аккумулятором (см. …lw), причём, в данном случае (применение операторов high
и low), константой является числовое значение
 либо старшего байта PC-адреса той команды, на которой выставлена метка
(если применяется оператор high),
 либо младшего байта PC-адреса той команды, на которой выставлена метка
(если применяется оператор low),
Эти числовые значения могут быть
- либо банально скопированы в регистр W (команда movlw),
- либо, после осуществления какой-то логической операции (addlw, sublw, andlw, iorlw, xorlw),
сохранены в W.
“Привяжусь” к конкретике “вышележащей” программы.
movlw high PC_ADR означает то, что в регистр W копируется числовое значение
старшего байта адреса той PC-ячейки, в которой “лежит” первая команда таблицы
вычисляемого перехода, помеченная меткой PC_ADR (откровенный намёк на PC-адрес, но можно
придумать и другое название).
Для того чтобы это имело место быть, нужно, после применения оператора high
(movlw high PC_ADR), просто скопировать содержимое W в регистр PCLATH (movwf PCLATH),
в результате чего, в регистре PCLATH, заранее и “за один присест”, будут
установлены именно те значения PC-страницы и PC-блока, которые необходимы для
осуществления “безбяковой” работы в верхней части таблицы, начиная от первой её
команды и до той команды, которая “примыкает” к “межблочной границе”.
Короче, “полдела сделано”, но ведь есть ещё и та часть таблицы вычисляемого
перехода, которая “дислоцируется” ниже “межблочной границы”.
Если “зайти туда” с указанным выше числовым значением PCLATH, то будет
неумолимый, “вычисляемопереходный глюк”.
Значит, в целях его недопущения, перед тем, как “зайти туда”, нужно произвести
программный инкремент содержимого регистра PCLATH.
Критерием этого инкремента является факт переноса (переход от FFh к 00h), из младшего
байта, в старший.
То есть, нужно считать младший байт PC-адреса той PC-ячейки, в которой “лежит”
первая команда таблицы вычисляемого перехода, помеченная меткой PC_ADR (то есть,
обратиться к оператору low), приплюсовать к нему “прыжковое” число и посмотреть, что из
этого выйдет (перенос есть или его нет? Это “епархия” бита С регистра STATUS).
 Если факта переноса не “засечено”, значит инкрементировать содержимое
регистра PCLATH не нужно, и дальнейший “прыг-скок” будет осуществлён в
“границах” верхней части таблицы (той, которая “дислоцируется” выше “межблочной границы”).
 Если факт переноса “засечён”, значит инкрементировать содержимое регистра
PCLATH очень даже нужно, и дальнейший “прыг-скок” будет осуществлён в
“границах” нижней части таблицы (той, которая “дислоцируется” ниже “межблочной границы”).
Это и программно реализовано, причём, достаточно изящно/компактно (см. текст программы).
А именно:
1. С целью осуществления “дальнейшей математики” (условно), “прыжковое” число
копируется в регистр W (movf Reg,W . Содержимое регистра Reg остаётся неизменным).
2. Далее, “прыжковое” число суммируется с числовым значением младшего байта
PC-адреса той PC-ячейки, в которой “лежит” первая команда таблицы
вычисляемого перехода, помеченная меткой PC_ADR, а результат сохраняется в
регистре W (addlw low PC_ADR).
3. Теперь остаётся только проверить факт наличия (С=1) или отсутствия (С=0)
переноса (btfsc STATUS,C . Команда addlw воздействует на флаг С).
4. Если перенос обнаружен, то PCLATH + 1 = ... (работа ниже “межблочной границы”).
5. Если перенос не обнаружен, то инкремент содержимого PCLATH не
осуществляется (работа выше “межблочной границы”).
6. После отработки любого из этих сценариев, “прыжковое” число копируется в
регистр W.
Далее осуществляется стандартная работа с таблицей вычисляемого перехода, но с
весьма существенным уточнением:
“межблочные/межстраничные границы à по-барабану”.

26
Примечание: например, в PC, адрес метки 0102h. В этом случае, в окне Program Memory MPLAB-версии 8.10:

- команда movlw high PC_ADR отобразится как movlw 0x1,


- а команда addlw low PC_ADR отобразится как addlw 0x2.

Применение, в таблицах вычисляемых переходов, директивы dt


На примере одной, конкретной таблицы, которой условно “присвоен статус матери”,
можно “въехать в общую, дочернюю тенденцию” (напоминаю о том, что “мать рожает дочерей”, а не
наоборот).
Та таблица вычисляемого перехода, с которой Вы знакомы (см. выше. “Привяжусь” к ней),
имеет неоспоримый/уважаемый “статус матери”:
“Материнский” вариант таблицы
TABLE addwf PCL,F ; PCL + W = ...
retlw b'00111111' ; ..FEDCBA -> символ “0” 3Fh
retlw b'00000110' ; .....CB. -> символ “1” 06h
retlw b'01011011' ; .G.ED.BA -> символ “2” 5Bh
retlw b'01001111' ; .G..DCBA -> символ “3” 4Fh
retlw b'01100110' ; .GF..CB. -> символ “4” 66h
retlw b'01101101' ; .GF.DC.A -> символ “5” 6Dh
retlw b'01111101' ; .GFEDC.A -> символ “6” 7Dh
retlw b'00000111' ; .....CBA -> символ “7” 07h
retlw b'01111111' ; .GFEDCBA -> символ “8” 7Fh
retlw b'01101111' ; .GF.DCBA -> символ “9” 6Fh

Так как, в части касающейся команд retlw, “в глаза бросается” их повторение, то


разработчики “сваяли сервис”. В виде директивы dt .
Она, образно и упрощённо выражаясь, “оптово авторожАет” команды retlw, причём,
“укомплектованные” соответствующими числами, при условии того, что эти числа, через
запятые, “прописаны” правее директивы dt (в 3-м столбце текста программы).
Соответственно, в текстовом редакторе MPLAB, текст “вышележащей” таблицы
“ужимается” и становится более компактным.
Например (“те же яйца, только в профиль”).
Вариант 1
TABLE addwf PCL,F ; PCL + W = ...
dt 0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F

Вариант 2
TABLE addwf PCL,F ; PCL + W = ...
dt 0x3F,0x06,0x5B,0x4F,0x66
dt 0x6D,0x7D,0x07,0x7F,0x6F

Вариант 3
TABLE addwf PCL,F ; PCL + W = ...
dt 0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07
dt 0x7F,0x6F

И так далее (есть и другие оргварианты), но, не смотря на “бросающиеся в глаза”,


формальные различия, это одно и то же.

Мотивация
При прочих, равных условиях, PC-разложение, на команды (это то, что “лежит” не в
текстовом редакторе MPLAB, а в окне Program Memory, то есть, в памяти программ),
любого из этих
вариантов неумолимо сводится к “Материнскому” варианту таблицы
(можете проверить в симуляторе).

Таким образом, при прочих, равных условиях, применение директивы dt (а также и других,
аналогичных директив) позволяет ”компактизировать” текст программы, “настуканный” в
текстовом редакторе MPLAB, и не более того, а на “мать, лежащую” в памяти
программ, это не распространяется (вспомнил о поговорке “яйца курицу не учат”).

27
Она как была “матерью”, так ей и останется (кстати, и по жизни, в буквальном смысле, это так),
но почему-то не все понимают эту банальность (уставшую истину), и не только в
приложении к директиве dt, но и в приложении к другому.
Не смотря на это, применение директивы dt удобно (при условии качественного “въезда в
сермяжную суть матери”!!!).
Соответственно, в дальнейшем, я её и буду применять.

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

Варианты программных процедур вычисляемых переходов, применяемых при


работе с ЖК-модулями (на основе м/к HD44780).

Стандартные и универсальные ПП вычисляемых переходов


Сначала нужно кое-что пояснить.
В “вышележащих” программах, с целью обеспечения “комфортабельного въезда” в
работу процедуры вычисляемого перехода, осуществлён простой, последовательный,
программный (инкрементный) “перебор прыжковых чисел”.
Необходимо отметить, что в приложении к работе с 7-сегментными индикаторами, это
слегка не корректно, так как, в большинстве случаев, на индикацию, в 7-сегментный
индикатор (или в их линейку), “в динамике”, выводятся (в виде символов цифр. Он под них “заточен”)
результаты замеров “того-сего”, причём, не в строго заданном порядке (как в ранее
рассмотренных случаях), а по принципу “что есть в реале, то и выводится” (например, замеры
частоты, температуры и т.д., значения которых, во времени и в зависимости от многочисленных и достаточно
“анархичных” факторов, изменяются).
С учётом сказанного, речь идёт не о строго заданном, последовательном порядке
формирования “прыжковых” чисел (например, как в программах №1, 2, 3 : 0, 1, 2, 3, 4, 5, ...),
а о произвольном порядке их формирования (например: 8, 0, 5, 1, 0, 7, ...).
Соответственно, во имя обеспечения должной корректности, в текстах программ №1,2,3,
целесообразно заменить (это на любителя. Можно и не заменять. Главное - понять суть) программную
процедуру, с названием Имитация “добывания прыжковых” данных, на это:

Для программ №1 и №2 :
;----------------------------------------------------------------------------------------------------
; ПОЯСНЕНИЕ: “прыжковое” число должно быть сформировано (теми или иными способами) до вызова ПП
; TABLE, а непосредственно перед этим вызовом, скопировано в аккумулятор (W).
; В рассматриваемом случае (таблица из 10-ти строк), диапазон “прыжковых” чисел: от 0 до 9
; (включительно), и из этого диапазона “выходить” нельзя, а иначе рабочая точка программы “прыгнет за
; границу” таблицы вычисляемого перехода (“глюк”).
; А если рассуждать “глобально”, то диапазон “прыжковых” чисел: от 0 до N-1 (включительно),
; где N – число строк таблицы вычисляемого перехода.
;----------------------------------------------------------------------------------------------------
; Примитивная имитация произвольного порядка формирования “прыжковых” чисел.
;---------------------------------------------------------------------------
movlw 4 ; В данном случае, задано число 4 (“прыжок” будет
; осуществлён в 5-ю строку таблицы вычисляемого
; перехода), но можно записать и другое число.
;---------------------------------------------------------------------------
call TABLE ; Вызов ПП TABLE.
; --> Возврат по стеку из ПП TABLE (“добытое лежит” в W).
movwf PORTB ; Вывод цифры на индикацию.
; ................................

Для программы №3 :
То же самое, что и для программ №1 и №2 (см. выше), но с копированием “прыжкового”
числа в регистр Reg:
;---------------------------------------------------------------------------
; Примитивная имитация произвольного порядка формирования “прыжковых” чисел.
;---------------------------------------------------------------------------
movlw 4 ; В данном случае, задано число 4 (“прыжок” будет
movwf Reg ; осуществлён в 5-ю строку таблицы вычисляемого
; перехода), но можно записать и другое число.

28
;---------------------------------------------------------------------------
call TABLE ; Вызов ПП TABLE.
; --> Возврат по стеку из ПП TABLE (“добытое лежит” в W).
movwf PORTB ; Вывод цифры на индикацию.
; ................................

Следует сказать, что в этих случаях, смена “прыжковых” чисел осуществляется


не “в автомате”, а “вручную”, что, естественно, не очень комфортно.
Перехожу к ЖК-модулям.
Суть только что сказанного относится и к ним (естественно, с учётом различных специфик), но в
отличие от 7-сегментных индикаторов, которые преимущественно “заточены” под
отображение символов цифр (можно отобразить и символы букв, но в очень малом количестве),
ЖК-модули “заточены”:
- и под отображение символов цифр,
- и под отображение символов букв (если ЖК-модуль русифицирован, то аж на двух языках:
английском и русском. Буквы и прописные, и строчные) ,
- и под отображение символов знаков препинания,
- и под отображение других символов.
Все эти символы, образно выражаясь, “лежат” в таблице знакогенератора ЖК-модуля
(их коды в неё энергонезависимо записываются на заводе-изготовителе) и имеют “свои индивидуальные”
адреса.
Частенько бывает необходимым вывести на индикацию какую-то “фиксированную”
(в каком-то достаточно продолжительном интервале времени) надпись, например: Частота: <справа -
результат замера частоты> или, допустим, ЗАЩИТА ВЫКЛЮЧЕНА (и так далее. Вариантов очень
много).
К последней надписи и “привяжусь” (в приложении к ЖК-модулям 2х16).

Примечание: для того чтобы облегчить составление кода надписи, Пётр Высочанский придумал программу-конвертор
ConverterForHD44780, архив которой прилагается к этой статье.
После распаковки архива, нужно щёлкнуть по распакованному файлу и согласится с созданием файла, хранящего
данные о профилях. После этого, файл будет создан и откроется окно программы, которое разделено на 2 части.
В верхней части, нужно “настучать” вожделеемые символы, а затем Преобразование à Преобразовать…
После этого, в нижней части окна программы, “возникнет” содержимое таблицы вычисляемого перехода, которое
подлежит копированию в текст программы (можете свериться с таблицей русифицированного знакогенератора).

Надпись ЗАЩИТА ВЫКЛЮЧЕНА занимает 16 знакомест (15 символов букв и символ “пусто” или
“пробел”. Знакоместо - место визуальной “дислокации” адресно выбранного символа),
значит, задействована
вся строка ЖК-дисплея.
Соответственно, нужно начать вывод символов на индикацию с первого знакоместа
выбранной строки (с сАмого левого), а закончить в последнем (в сАмом правом).
В том случае, если не предпринимается “спецмер” по адресному “перескоку” через
N-знакомест (возможны и такие случаи), то по окончании вывода символа на индикацию
(в предыдущее знакоместо) , происходит автоматический выбор следующего знакоместа, что
существенно оргупрощает процесс вывода данных на индикацию.
Таким образом, в рассматриваемом случае, речь идёт не о произвольном порядке
формирования “прыжковых” чисел, а о последовательном, строго заданном
порядке формирования “прыжковых” чисел.
При этом, “PC-странично-блочная” специфика никуда не исчезла и полномасштабно
соблюдается.
Ниже приведён текст программы, который, по сути, является аналогом программы №2
(“инструкцию по эксплуатации” повторять не буду. См. выше), но только в приложении к ЖК-модулю
(на индикацию, в строку ЖК-дисплея, выводится надпись ЗАЩИТА ВЫКЛЮЧЕНА) :

Программа №4 (для ЖК-модуля на основе м/к HD44780)


;***************************************************************************************************
; Программа, позволяющая проверить работу вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.

29
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Count ; Счётчик проходов.
endc ; Конец блока.
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; ВНИМАНИЕ! ПРИ ПРОВЕРКЕ, ДОЛЖНА РАБОТАТЬ ТОЛЬКО ОДНА ИЗ “НИЖЕЛЕЖАЩИХ” ДИРЕКТИВ org ...
; ОСТАЛЬНЫЕ ДОЛЖНЫ БЫТЬ ЗАБЛОКИРОВАНЫ + ВЫПОЛНЕНИЕ “НИЖЕЛЕЖАЩЕЙ” ...!!!! ИНСТРУКЦИИ !!!...
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; Варианты “безбяковой” работы.
; Можно назначить и другие PC-адреса, лишь-бы не выйти за “границы” блока.
;-----------------------------------------------------------------------------------------------
;;; org 0 ; Первая команда программы “дислоцируется” в блоке 0.
;;; org 100h ; Первая команда программы “дислоцируется” в блоке 1.
;;; org 200h ; Первая команда программы “дислоцируется” в блоке 2.
;;; org 300h ; Первая команда программы “дислоцируется” в блоке 3.
;-----------------------------------------------------------------------------------------------
; Варианты “бяковой” работы: “межблочная граница” проходит через таблицу вычисляемого перехода
; (см. окно Program Memory), но при желании, её можно адресно сместить.
;-----------------------------------------------------------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 0-го и 1-го блоков.
;---------------------------------------------
org 0F9h ; Текст программы адресно смещён так, что верхняя часть ПП
; TABLE “лежит” в блоке 0, а нижняя её часть, в блоке 1.
;---------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 1-го и 2-го блоков.
;---------------------------------------------
;;; org 1F9h ; Текст программы адресно смещён так, что верхняя часть ПП
;;; ; TABLE “лежит” в блоке 1, а нижняя её часть, в блоке 2.
;---------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 2-го и 3-го блоков.
;---------------------------------------------
;;; org 2F9h ; Текст программы PC-смещён так, что верхняя часть ПП
;;; ; TABLE “лежит” в блоке 2, а нижняя её часть, в блоке 3.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

; ................................ ; Часть программы (кроме случая org 0).


;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Вызываемая ПП преобразования двоичного кода в код символа, выводимого на индикацию в дисплей
; ЖК-модуля. Линии данных ЖК-модуля подключены к выводам порта В.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; В таблице “лежит” адресный код символов, формирующих надпись ЗАЩИТА ВЫКЛЮЧЕНА
;------------------------------------------------------------------------------
TABLE addwf PCL,F ; PCL + W = ...
dt 0xA4,0x41,0xE2,0xA5,0x54,0x41,0x20,0x42 ; ЗАЩИТА
dt 0xAE,0x4B,0xA7,0xB0,0xAB,0x45,0x48,0x41 ; ВЫКЛЮЧЕНА
;-----------------------------------------------------------------------------------------------
; ...................................
;***********************************************************************************************
; Начало программы.
;***********************************************************************************************
START bsf STATUS,RP0 ; Переход в 1-й банк. <-- ТОЧКА ОСТАНОВКИ
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Переход в 0-й банк.

;===================================================================================================
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИНСТРУКЦИЯ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;===================================================================================================
; Если разблокировано org 0 или org 0F9h, то заблокировать всё то, что ниже выделено красным цветом.
;---------------------------------------------------------------------------------------------------
; Разблокировать, если разблокировано org 100h или org 1F9h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 1 ; Так как 1-я команда ПП TABLE “лежит”
;;; movwf PCLATH ; в блоке 1, то выбор блока 1.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 200h или org 2F9h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 2 ; Так как 1-я команда ПП TABLE “лежит”
;;; movwf PCLATH ; в блоке 2, то выбор блока 2.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 300h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 3 ; Так как 1-я команда ПП TABLE “лежит”
;;; movwf PCLATH ; в блоке 3, то выбор блока 3.
;==================================================================================================

30
; Процедура вывода на индикацию надписи ЗАЩИТА ВЫКЛЮЧЕНА.
;==================================================================================================
nop ; Символизирует управление ЖК-модулем: установка курсора
; в начало выбранной строки дисплея (в крайнее левое знакоместо)
; и последующее стробирование.
movlw .16 ; Запись, в регистр Count, количества
movwf Count ; выводимых, в строку, символов.
SNOVA movf Count,W ; Count -> W.
sublw .16 ; .16 - Count = ... (Результат -> W).
call TABLE ; Вызов ПП TABLE.
;----> Возврат по стеку из ПП TABLE (“добытое лежит” в W).
movwf PORTB ; Адресный выбор ячейки ЖК-знакогенератора (подготовка
; к выводу текущего символа на индикацию).
nop ; Символизирует управление ЖК-модулем: установка режима
; записи данных и последующее стробирование (т.е. фактический
; вывод символа на индикацию).
decfsz Count,F ; На индикацию выведены все символы или не все ?
goto SNOVA ; Если не все, то выводим на индикацию следующий символ.
goto $ ; Если все, то “мертвяк” (если нужна многоцикличность,
; то, например, goto START).
;***************************************************************************************************
end ; Конец программы.

Обратите внимание на то, что в тексте программы есть “символические” NOPы,


которые (а также и комментарии к ним) выделены тёмно-зелёным цветом.
Мотивация: в целях концентрации Вашего внимания на вычисляемых переходах, я
сознательно не стал “загромождать” текст программы спецификой управления
ЖК-модулем (это отдельный разговор), но “перечень” управляющих действий указал в
комментариях (то же самое относится и к “нижележащим” программам).
В таблицу вычисляемого перехода, в последовательном порядке следования символов
букв надписи ЗАЩИТА ВЫКЛЮЧЕНА (по направлению слева направо), с привлечением
директивы dt , “заложены” адреса ячеек ЖК-знакогенератора, в которых “лежат”
символы соответствующих букв (можете проконтролировать по таблице русифицированного знакогенератора) .
Принцип “перебора прыжковости”, по сути, такой же, как и в программах 1,2,3, но
“перебираются” не 10 вариантов “прыжковости”, а 16, плюс, специфика работы
с ЖК-модулем.
Программа, в состав которой входит универсальная ПП вычисляемого перехода,
“адаптированная под нужды” ЖК-модуля, которая, по сути, является аналогом
программы №3 (“инструкцию по эксплуатации” повторять не буду. См. выше) выглядит так:

Программа №5 (для ЖК-модуля на основе м/к HD44780)


;***************************************************************************************************
; Программа, позволяющая проверить работу ПП универсального вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; “Прыжковый” регистр.
Count ; Счётчик проходов.
endc ; Конец блока.
;-----------------------------------------------------------------------------------------------
; “Межблочная граница” проходит через таблицу вычисляемого перехода (см. окно Program Memory),
; но при желании, её можно адресно сместить. При проверке, должна работать только одна из
; “нижележащих” директив org ... , а остальные должны быть заблокированы.
;-----------------------------------------------------------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 0-го и 1-го блоков.
;---------------------------------------------
org 0F2h ; Текст программы адресно смещён так, что верхняя часть ПП
; TABLE “лежит” в блоке 0, а нижняя её часть, в блоке 1.
;---------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 1-го и 2-го блоков.
;---------------------------------------------
;;; org 1F2h ; Текст программы адресно смещён так, что верхняя часть ПП

31
;;; ; TABLE “лежит” в блоке 1, а нижняя её часть, в блоке 2.
;---------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 2-го и 3-го блоков.
;---------------------------------------------
;;; org 2F2h ; Текст программы PC-смещён так, что верхняя часть ПП
;;; ; TABLE “лежит” в блоке 2, а нижняя её часть, в блоке 3.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

; ................................ ; Часть программы.


;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Вызываемая ПП преобразования двоичного кода в код символа, выводимого на индикацию в дисплей
; ЖК-модуля. Линии данных ЖК-модуля подключены к выводам порта В.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; В таблице “лежит” адресный код символов, формирующих надпись ЗАЩИТА ВЫКЛЮЧЕНА
;------------------------------------------------------------------------------
TABLE movlw high PC_ADR ; Установка (через W), в PCH (посредством PCLATH), числового
movwf PCLATH ; значения старшего байта адреса той команды, которая помечена
; меткой PC_ADR.
;---------------------------------------------------
; Будем “прыгать” в текущий PC-блок или в следующий?
;---------------------------------------------------
movf Reg,W ; “Прыжковое” число копируется в W.
addlw low PC_ADR ; Суммирование "прыжкового" числа и числового значения младшего
; байта адреса той команды, которая помечена меткой PC_ADR.
btfsc STATUS,C ; Перенос был или нет?
incf PCLATH,F ; Если перенос был (соответствует работе в следующем PC-блоке),
; то PCLATH+1=... (то есть, PCH+1=...).
; Если переноса не было (соответствует работе в текущем
; PC-блоке), то команда инкремента не исполняется.
movf Reg,W ; “Прыжковое” число копируется в W.
;------------------------------------
; Таблица вычисляемого перехода.
;------------------------------------
addwf PCL,F ; PCL + W = ...
PC_ADR dt 0xA4,0x41,0xE2,0xA5,0x54,0x41,0x20,0x42 ; ЗАЩИТА
dt 0xAE,0x4B,0xA7,0xB0,0xAB,0x45,0x48,0x41 ; ВЫКЛЮЧЕНА
;-----------------------------------------------------------------------------------------------
; ................................ ; Символизирует
; ................................ : часть программы.
;********************************************************************************************
; Начало программы.
;********************************************************************************************
START bsf STATUS,RP0 ; Переход в 1-й банк. <-- ТОЧКА ОСТАНОВКИ
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Переход в 0-й банк.
;==================================================================================================
; Процедура вывода на индикацию надписи ЗАЩИТА ВЫКЛЮЧЕНА.
;==================================================================================================
nop ; Символизирует управление ЖК-модулем: установка курсора
; в начало выбранной строки дисплея (в крайнее левое знакоместо)
; и последующее стробирование.
movlw .16 ; Запись, в регистр Count, количества
movwf Count ; выводимых, в строку, символов.
SNOVA movf Count,W ; Count -> W.
sublw .16 ; .16 - Count = ... (Результат -> W).
movwf Reg ; W -> Reg.
call TABLE ; Вызов ПП TABLE.
;----> Возврат по стеку из ПП TABLE (“добытое лежит” в W).
movwf PORTB ; Адресный выбор ячейки ЖК-знакогенератора (подготовка
; к выводу текущего символа на индикацию).
nop ; Символизирует управление ЖК-модулем: установка режима
; записи данных и последующее стробирование (т.е. фактический
; вывод символа на индикацию).
decfsz Count,F ; На индикацию выведены все символы или не все ?
goto SNOVA ; Если не все, то выводим на индикацию следующий символ.
goto $ ; Если все, то “мертвяк” (если нужна многоцикличность,
; то, например, goto START).
;***************************************************************************************************
end ; Конец программы.

С учётом ранее сказанного, ничего принципиально нового, в этой программе, нет, за


исключением специфики управления ЖК-модулем.
Вариант универсальной подпрограммы вычисляемого перехода с
идентификатором конца таблицы

32
Теперь настало время познакомить Вас с ещё одним вариантом универсальной ПП
вычисляемого перехода, который, на мой взгляд, достаточно удобен/перспективен.
Вопрос: “А нельзя ли обойтись без заморочек с назначением количества прыжков, то
есть, назначить другой критерий окончания отработки таблицы вычисляемого
перехода”?
Ответ: можно, если закончить таблицу тем, что гарантированно не будет выводиться
на индикацию, но будет являться признаком окончания таблицы.
Если рассуждать комплексно (и в приложении к ЖК-модулям, и в приложении к графическим модулям),
то лучше всего “на эту роль подходит” число FFh.
В приложении к ЖК-модулям, это символ типа “чёрный прямоугольник” (все точки знакоместа
активны), востребованность которого очень низка, а в приложении к графическим
модулям, это “чёрный столбец” (все его точки активны).
В последнем случае, необходимо “конструировать” символы таким образом, чтобы
состояний FFh не было.
Вернусь к ЖК-модулям.
Программная реализация сказанного выглядит так (последнее число таблицы вычисляемого перехода,
а именно 0xFF, с целью привлечения внимания, выделено красным цветом):

Программа №6 (для ЖК-модуля на основе м/к HD44780)


;***************************************************************************************************
; Программа, позволяющая проверить работу ПП универсального вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; “Прыжковый” регистр.
endc ; Конец блока.
;-----------------------------------------------------------------------------------------------
; “Межблочная граница” проходит через таблицу вычисляемого перехода (см. окно Program Memory),
; но при желании, её можно адресно сместить. При проверке, должна работать только одна из
; “нижележащих” директив org ... , а остальные должны быть заблокированы.
;-----------------------------------------------------------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 0-го и 1-го блоков.
;---------------------------------------------
org 0F2h ; Текст программы адресно смещён так, что верхняя часть ПП
; TABLE “лежит” в блоке 0, а нижняя её часть, в блоке 1.
;---------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 1-го и 2-го блоков.
;---------------------------------------------
;;; org 1F2h ; Текст программы адресно смещён так, что верхняя часть ПП
;;; ; TABLE “лежит” в блоке 1, а нижняя её часть, в блоке 2.
;---------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 2-го и 3-го блоков.
;---------------------------------------------
;;; org 2F2h ; Текст программы PC-смещён так, что верхняя часть ПП
;;; ; TABLE “лежит” в блоке 2, а нижняя её часть, в блоке 3.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

; ................................ ; Часть программы.


;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Вызываемая ПП преобразования двоичного кода в код символа, выводимого на индикацию в дисплей
; ЖК-модуля. Линии данных ЖК-модуля подключены к выводам порта В.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; В таблице “лежит” адресный код символов, формирующих надпись ЗАЩИТА ВЫКЛЮЧЕНА
;------------------------------------------------------------------------------
TABLE movlw high PC_ADR ; Установка (через W), в PCH (посредством PCLATH), числового
movwf PCLATH ; значения старшего байта адреса той команды, которая помечена
; меткой PC_ADR.
;---------------------------------------------------
; Будем “прыгать” в текущий PC-блок или в следующий?
;---------------------------------------------------
movf Reg,W ; “Прыжковое” число копируется в W.
addlw low PC_ADR ; Суммирование "прыжкового" числа и числового значения младшего

33
; байта адреса той команды, которая помечена меткой PC_ADR.
btfsc STATUS,C ; Перенос был или нет?
incf PCLATH,F ; Если перенос был (соответствует работе в следующем PC-блоке),
; то PCLATH+1=... (то есть, PCH+1=...).
; Если переноса не было (соответствует работе в текущем
; PC-блоке), то команда инкремента не исполняется.
movf Reg,W ; “Прыжковое” число копируется в W.
;------------------------------------
; Таблица вычисляемого перехода.
;------------------------------------
addwf PCL,F ; PCL + W = ...
PC_ADR dt 0xA4,0x41,0xE2,0xA5,0x54,0x41,0x20,0x42 ; ЗАЩИТА
dt 0xAE,0x4B,0xA7,0xB0,0xAB,0x45,0x48,0x41,0xFF ; ВЫКЛЮЧЕНА
;-----------------------------------------------------------------------------------------------
; ................................ ; Символизирует
; ................................ : часть программы.
;********************************************************************************************
; Начало программы.
;********************************************************************************************
START bsf STATUS,RP0 ; Переход в 1-й банк. <-- ТОЧКА ОСТАНОВКИ
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Переход в 0-й банк.
;==================================================================================================
; Процедура вывода на индикацию надписи ЗАЩИТА ВЫКЛЮЧЕНА.
;==================================================================================================
nop ; Символизирует управление ЖК-модулем: установка курсора
; в начало выбранной строки дисплея (в крайнее левое знакоместо)
; и последующее стробирование.
clrf Reg ; Начинаем от ноля (Reg=0).
SNOVA call TABLE ; Вызов ПП TABLE.
; --> Возврат по стеку из ПП TABLE (“добытое лежит” в W).
movwf PORTB ; Адресный выбор ячейки ЖК-знакогенератора (подготовка
; к выводу текущего символа на индикацию).
sublw .255 ; .255 – W = ...
btfsc STATUS,Z ; Это .255 или другое число ?
goto VIHOD ; Если .255, то окончание вывода надписи на индикацию
; и выход из процедуры.
; Если другое число, то далее
; (продолжение вывода надписи на индикацию).
nop ; Символизирует управление ЖК-модулем: установка режима
; записи данных и последующее стробирование (т.е. фактический
; вывод символа на индикацию).
incf Reg,F ; Reg + 1 = ... Результат -> Reg.
goto SNOVA ; Продолжаем перебор далее.
;------------------------------------
VIHOD goto $ ; Если все, то “мертвяк” (если нужна многоцикличность,
; то, например, goto START).
;***************************************************************************************************
end ; Конец программы.

Обратите внимание на то, что в этой программе не назначается число проходов.


Таблица вычисляемого перехода будет последовательно отрабатываться до тех пор,
пока рабочая точка программы не “споткнётся” о признак конца таблицы (0xFF), что
означает окончание вывода надписи (или её части. В жизни всякое бывает) на индикацию.
Получается “дёшево и сердито”.
Если необходимо отработать всю таблицу, то ,0xFF нужно “врезать” в конец таблицы
вычисляемого перехода, а если её часть, то просто “передислоцируйте” ,0xFF в
соответствующее “место”.
Программную процедуру, с названием Процедура вывода на индикацию надписи ЗАЩИТА ВЫКЛЮЧЕНА ,
можно реализовать двумя способами.
Если задаться целью не допустить копирования числа FFh (.255) в регистр PORTB, то
это вполне можно сделать, но зачем, ведь это копирование не есть вывод символа на
индикацию, а есть предустановка.
Символ выводится на индикацию по факту “прохождения строба” (стробирующего импульса. Это
что-то типа “ввода в эксплуатацию” ранее подготовленного) , формирование которого происходит
позднее, а именно, в том “месте”, которое символически помечено нижним (по тексту
программы) NOPом.
Если между movf PORTB и этим NOPом “врезать” процедуру проверки на
наличие/отсутствие числа FFh, то по факту обнаружения числа FFh, можно легко
обойти нижний NOP (одновременно, это и выход из процедуры вывода надписи на индикацию), что
предотвращает “паразитный” вывод на индикацию символа “чёрный прямоугольник”.
Такое программное решение содержит наименьшее количество команд и является, на
мой взгляд, наиболее оптимальным.
34
Варианты программных процедур вычисляемых переходов, применяемых при
работе с графическими модулями (на основе м/к KS0108).

Стандартные и универсальные ПП вычисляемых переходов


Сразу же следует особо отметить то, что “механизм” вывода данных на индикацию, в
дисплей графического модуля, более сложен, нежели “механизм” вывода данных на
индикацию, в дисплей ЖК-модуля.

Мотивация: одна дисплейная строка ЖК-модуля типа, например, 2х16, заполняется за


16 “знаковоместовых” приёмов (“познакоместная” работа), а одна дисплейная строка
графического модуля (это две “однономерные” строки кристаллов, “вытянутые в одну линию”) типа,
например, 128х64, заполняется за 128 “столбцовых” приёмов (“постолбцовая” работа, а заодно
и “покристальная”).

Один стандартный, “графомодульный” столбец “привязан” к одному управляющему


байту и состоит из 8-ми (по количеству битов в байте) вертикально “выстроенных” пикселей
(в основном, пиксель имеет форму квадрата), любой из которых, в зависимости от состояния
“привязанного” к нему, управляющего бита, может быть либо активным (визуально
ощущаемым), либо пассивным (визуально не ощущаемым).
Естественно, что и в этом случае, знакоместА сформировать можно, но только с
помощью программных (то есть, “гибких”) средств, и при прочих, равных условиях, их
количество (в строке) может “варьироваться” в зависимости от конкретной
“графоконструкции” символа/символов.
Следует отметить также и то, что при таком “раскладе” (относительно большое количество
“перелопачиваемых” столбцов), и в условиях отсутствия “спецсредств”, создание содержимого
таблиц вычисляемых переходов, работающих с графическим модулем, является
достаточно трудоёмким занятием (если надписей много, то эта работа чем-то напоминает “египетский
труд”).
Эту проблему, путём создания виндопроги KS0108, эффективно решил Пётр
Высочанский (отличная работа. Претензий нет, хотя я и привередливый. Это своеобразный знак качества,
причём, без кавычек).
Эта виндопрога есть и на CD, и на моём сайте (с “инструкцией по эксплуатации”. Она проста).
Для тех из Вас, кто работает с двухкристальными, графическими модулями на основе
м/к KS0108, это самая натуральная “палочка-выручалочка”, которая существенно
упрощает работу (желающие могут поместить эту виндопрогу в папку этой статьи).
Ну а теперь ближе к делу.
Работаем с той же надписью ЗАЩИТА ВЫКЛЮЧЕНА, но только в приложении к
графическому модулю.
Для того чтобы показать Вам конечный результат её “конструирования” (в виндопроге
Петра), данная статья “укомплектована” CHR-файлом этой надписи, а также и ранее
созданными мной CHR-файлами библиотек русских и английских символов
(при “конструировании” надписи ЗАЩИТА ВЫКЛЮЧЕНА, использовалась CHR-библиотека русских символов).
Ниже приведён текст программы, который, по сути, является аналогом программы №2
(для 7-сегментных индикаторов) и программы №4 (для ЖК-модулей на основе м/к HD44780. “Инструкцию по
эксплуатации” повторять не буду. См. выше), но только в приложении к графическому модулю на
основе м/к KS0108 :

Программа №7 (для графического модуля на основе м/к KS0108)


;***************************************************************************************************
; Программа, позволяющая проверить работу вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------

35
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Count ; Счётчик проходов.
endc ; Конец блока.
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; ВНИМАНИЕ! ПРИ ПРОВЕРКЕ, ДОЛЖНА РАБОТАТЬ ТОЛЬКО ОДНА ИЗ “НИЖЕЛЕЖАЩИХ” ДИРЕКТИВ org ...
; ОСТАЛЬНЫЕ ДОЛЖНЫ БЫТЬ ЗАБЛОКИРОВАНЫ + ВЫПОЛНЕНИЕ “НИЖЕЛЕЖАЩЕЙ” ...!!!! ИНСТРУКЦИИ !!!...
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; Варианты “безбяковой” работы.
; Можно назначить и другие PC-адреса, лишь-бы не выйти за “границы” блока.
;-----------------------------------------------------------------------------------------------
;;; org 0 ; Первая команда программы “дислоцируется” в блоке 0.
;;; org 100h ; Первая команда программы “дислоцируется” в блоке 1.
;;; org 200h ; Первая команда программы “дислоцируется” в блоке 2.
;;; org 300h ; Первая команда программы “дислоцируется” в блоке 3.
;-----------------------------------------------------------------------------------------------
; Варианты “бяковой” работы: “межблочная граница” проходит через таблицу вычисляемого перехода
; (см. окно Program Memory), но при желании, её можно адресно сместить.
;-----------------------------------------------------------------------------------------------
; Через таблицу вычисляемого перехода TABLE_1 проходит
; “межблочная граница” 0-го и 1-го блоков.
;------------------------------------------------------
org 0F9h ; Текст программы адресно смещён так, что верхняя часть ПП
; TABLE_1 “лежит” в блоке 0, а нижняя её часть, в блоке 1.
;------------------------------------------------------
; Через таблицу вычисляемого перехода TABLE_1 проходит
; “межблочная граница” 1-го и 2-го блоков.
;------------------------------------------------------
;;; org 1F9h ; Текст программы адресно смещён так, что верхняя часть ПП
;;; ; TABLE_1 “лежит” в блоке 1, а нижняя её часть, в блоке 2.
;------------------------------------------------------
; Через таблицу вычисляемого перехода TABLE_1 проходит
; “межблочная граница” 2-го и 3-го блоков.
;------------------------------------------------------
;;; org 2F9h ; Текст программы PC-смещён так, что верхняя часть ПП
;;; ; TABLE_1 “лежит” в блоке 2, а нижняя её часть, в блоке 3.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

; ................................ ; Часть программы.


;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Вызываемые ПП преобразования двоичного кода в код столбцов символов, выводимых на индикацию в
; дисплей графического модуля. Линии данных графического модуля подключены к выводам порта В.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; На индикацию, в строку дисплея, в 2 приёма (так как строка дисплея состоит из двух “однономерных”
; строк кристаллов), выводится надпись ЗАЩИТА ВЫКЛЮЧЕНА .
;====================================================================================================
; Для надписи ЗАЩИТА В .
;====================================================================================================
TABLE_1 addwf PCL,F ; PCL + W = ...
dt 0x00,0x00,0x44,0x82,0x92,0x92,0x6C,0x00 ; символ З
dt 0x00,0x00,0xFC,0x22,0x22,0x22,0xFC,0x00 ; символ А
dt 0x00,0x00,0x7E,0x40,0x7E,0x40,0x7E,0xC0 ; символ Щ
dt 0x00,0x00,0xFE,0x20,0x10,0x08,0xFE,0x00 ; символ И
dt 0x00,0x00,0x02,0x02,0xFE,0x02,0x02,0x00 ; символ Т
dt 0x00,0x00,0xFC,0x22,0x22,0x22,0xFC,0x00 ; символ А
dt 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; символ пусто (пробел)
dt 0x00,0x00,0xFE,0x92,0x92,0x92,0x6C,0x00 ; символ В
;====================================================================================================
; Для надписи ЫКЛЮЧЕНА .
;====================================================================================================
TABLE_2 addwf PCL,F ; PCL + W = ...
dt 0x00,0xFE,0x88,0x88,0x70,0x00,0xFE,0x00 ; символ Ы
dt 0x00,0x00,0xFE,0x10,0x28,0x44,0x82,0x00 ; символ К
dt 0x00,0x40,0x82,0x7E,0x02,0x02,0xFE,0x00 ; символ Л
dt 0x00,0xFE,0x10,0x7C,0x82,0x82,0x7C,0x00 ; символ Ю
dt 0x00,0x00,0x0E,0x10,0x10,0x10,0xFE,0x00 ; символ Ч
dt 0x00,0x00,0xFE,0x92,0x92,0x92,0x82,0x00 ; символ Е
dt 0x00,0x00,0xFE,0x10,0x10,0x10,0xFE,0x00 ; символ Н
dt 0x00,0x00,0xFC,0x22,0x22,0x22,0xFC,0x00 ; символ А
;-----------------------------------------------------------------------------------------------
; ................................ ; Символизирует
; ................................ : часть программы.
;********************************************************************************************
; Начало программы.
;********************************************************************************************
START bsf STATUS,RP0 ; Переход в 1-й банк. <-- ТОЧКА ОСТАНОВКИ
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Переход в 0-й банк.
;===================================================================================================

36
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИНСТРУКЦИЯ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;===================================================================================================
; Если разблокировано org 0 или org 0F9h, то заблокировать всё то, что ниже выделено красным цветом.
;---------------------------------------------------------------------------------------------------
; Разблокировать, если разблокировано org 100h или org 1F9h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 1 ; Так как 1-я команда ПП TABLE_1 “лежит”
;;; movwf PCLATH ; в блоке 1, то выбор блока 1.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 200h или org 2F9h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 2 ; Так как 1-я команда ПП TABLE_1 “лежит”
;;; movwf PCLATH ; в блоке 2, то выбор блока 2.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 300h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 3 ; Так как 1-я команда ПП TABLE_1 “лежит”
;;; movwf PCLATH ; в блоке 3, то выбор блока 3.
;==================================================================================================
; Процедура вывода на индикацию надписи ЗАЩИТА ВЫКЛЮЧЕНА.
; Например, выводим эту надпись в 1-ю дисплейную строку.
;==================================================================================================
; Вывод на индикацию, в 1-ю строку 1-го кристалла, надписи ЗАЩИТА В.
;-------------------------------------------------------------------
nop ; Символизирует управление графическим модулем: выбор 1-го
; (левого) кристалла, выбор 1-й страницы, выбор 1-го столбца.
movlw .64 ; Запись, в регистр Count, количества
movwf Count ; выводимых, в “кристалльную” строку, столбцов.
SNOVA_1 movf Count,W ; Count -> W.
sublw .64 ; .64 - Count = ... (Результат -> W).
call TABLE_1 ; Вызов ПП TABLE_1.
;----> Возврат по стеку из ПП TABLE (“добытое лежит” в W).
movwf PORTB ; Подготовка к выводу на индикацию текущего столбца
; (предустановка кода столбца на 8-ми линиях данных
; графического модуля).
nop ; Символизирует управление графическим модулем: обеспечение
; условия работы с байтами данных, стробирование (т.е. вывод на
; индикацию), обеспечение условия работы с командами.
decfsz Count,F ; На индикацию выведены все столбцы или не все ?
goto SNOVA_1 ; Если не все, то выводим на индикацию следующий столбец.
; Если все, то далее (выводим на индикацию остаток надписи).
;-------------------------------------------------------------------
; Вывод на индикацию, в 1-ю строку 2-го кристалла, надписи ЫКЛЮЧЕНА.
;-------------------------------------------------------------------
nop ; Символизирует управление графическим модулем: выбор 2-го
; (правого) кристалла,выбор 1-й страницы, выбор 1-го столбца.
movlw .64 ;
movwf Count ;
SNOVA_2 movf Count,W ;
sublw .64 ; Аналогично,
call TABLE_2 ; только с вызовом
movwf PORTB ; ПП TABLE_2.
nop ;
decfsz Count,F ;
goto SNOVA_2 ;
;------------------------------------
goto $ ; Если все, то “мертвяк” (если нужна многоцикличность,
; то, например, goto START).
;***************************************************************************************************
end ; Конец программы.

В таблицы вычисляемого перехода, в последовательном порядке следования столбцов


надписи ЗАЩИТА ВЫКЛЮЧЕНА (по направлению слева направо), с привлечением директивы
dt , “заложены” 8-битные коды столбцов (это результат работы виндопроги Петра Высочанского).
Так как дисплейная строка “дислоцируется” в 2-х кристаллах, то имеются 2 процедуры
вывода надписи на индикацию (то есть, надпись выводится на индикацию в 2 приёма).
В каждом из этих случаев, “перебираются” 64 варианта “прыжковости” (по количеству
столбцов, входящих в состав одной строки кристалла), плюс, специфика работы с графическим
модулем (см. тёмно-зелёные NOPы).
Также следует отметить и то, что в “бяковых” случаях, текст программы адресно
PC-смещён таким образом, что “межблочная граница проходит” только через верхнюю
таблицу вычисляемого перехода (TABLE_1), а через нижнюю таблицу (TABLE_2) не
“проходит”, но этого вполне достаточно для того чтобы убедиться в наличии
“вычисляемопереходного глюка”.
37
Примечание: если эта “граница пройдёт” через нижнюю таблицу вычисляемого перехода, то, по сути, будет то же
самое, только, если так можно выразиться, “с оттяжкой исполнения приговора”.

Программа, в состав которой входит группа универсальных ПП вычисляемого


перехода, “адаптированная под нужды” графического модуля, которая, по сути,
является аналогом программы №3 (для 7-сегментных индикаторов) и программы №5 (для ЖК-
модулей на основе м/к HD44780. “Инструкцию по эксплуатации” повторять не буду. См. выше), но только в
приложении к графическому модулю на основе м/к KS0108, выглядит так:

Программа №8 (для графического модуля на основе м/к KS0108)


;***************************************************************************************************
; Программа, позволяющая проверить работу ПП универсального вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; “Прыжковый” регистр.
Count ; Счётчик проходов.
endc ; Конец блока.
;-----------------------------------------------------------------------------------------------
; “Межблочная граница” проходит через таблицу вычисляемого перехода (см. окно Program Memory),
; но при желании, её можно адресно сместить. При проверке, должна работать только одна из
; “нижележащих” директив org ... , а остальные должны быть заблокированы.
;-----------------------------------------------------------------------------------------------
; Через таблицу вычисляемого перехода TABLE_1 проходит
; “межблочная граница” 0-го и 1-го блоков.
;------------------------------------------------------
org 0F2h ; Текст программы адресно смещён так, что верхняя часть ПП
; TABLE_1 “лежит” в блоке 0, а нижняя её часть, в блоке 1.
;------------------------------------------------------
; Через таблицу вычисляемого перехода TABLE_1 проходит
; “межблочная граница” 1-го и 2-го блоков.
;------------------------------------------------------
;;; org 1F2h ; Текст программы адресно смещён так, что верхняя часть ПП
;;; ; TABLE_1 “лежит” в блоке 1, а нижняя её часть, в блоке 2.
;------------------------------------------------------
; Через таблицу вычисляемого перехода TABLE_1 проходит
; “межблочная граница” 2-го и 3-го блоков.
;------------------------------------------------------
;;; org 2F2h ; Текст программы адресно смещён так, что верхняя часть ПП
;;; ; TABLE_1 “лежит” в блоке 2, а нижняя её часть, в блоке 3.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

; ................................ ; Часть программы.


;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Вызываемые ПП преобразования двоичного кода в код столбцов символов, выводимых на индикацию в
; дисплей графического модуля. Линии данных графического модуля подключены к выводам порта В.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; На индикацию, в строку дисплея, в 2 приёма (так как строка дисплея состоит из двух “однономерных”
; строк кристаллов), выводится надпись ЗАЩИТА ВЫКЛЮЧЕНА .
;====================================================================================================
; Для надписи ЗАЩИТА В .
;====================================================================================================
TABLE_1 movlw high PC_ADR1; Установка (через W), в PCH (посредством PCLATH), числового
movwf PCLATH ; значения старшего байта адреса той команды, которая помечена
; меткой PC_ADR1.
;---------------------------------------------------
; Будем “прыгать” в текущий PC-блок или в следующий?
;---------------------------------------------------
movf Reg,W ; “Прыжковое” число копируется в W.
addlw low PC_ADR1 ; Суммирование "прыжкового" числа и числового значения младшего
; байта адреса той команды, которая помечена меткой PC_ADR1.
btfsc STATUS,C ; Перенос был или нет?
incf PCLATH,F ; Если перенос был (соответствует работе в следующем PC-блоке),
; то PCLATH+1=... (то есть, PCH+1=...).
; Если переноса не было (соответствует работе в текущем
; PC-блоке), то команда инкремента не исполняется.
movf Reg,W ; “Прыжковое” число копируется в W.
;----------------------------------------------------

38
; Таблица вычисляемого перехода под надпись ЗАЩИТА В.
;----------------------------------------------------
addwf PCL,F ; PCL + W = ...
PC_ADR1 dt 0x00,0x00,0x44,0x82,0x92,0x92,0x6C,0x00 ; символ З
dt 0x00,0x00,0xFC,0x22,0x22,0x22,0xFC,0x00 ; символ А
dt 0x00,0x00,0x7E,0x40,0x7E,0x40,0x7E,0xC0 ; символ Щ
dt 0x00,0x00,0xFE,0x20,0x10,0x08,0xFE,0x00 ; символ И
dt 0x00,0x00,0x02,0x02,0xFE,0x02,0x02,0x00 ; символ Т
dt 0x00,0x00,0xFC,0x22,0x22,0x22,0xFC,0x00 ; символ А
dt 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; символ пусто (пробел)
dt 0x00,0x00,0xFE,0x92,0x92,0x92,0x6C,0x00 ; символ В
;====================================================================================================
; Для надписи ЫКЛЮЧЕНА .
;====================================================================================================
TABLE_2 movlw high PC_ADR2;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR2 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;
;----------------------------------------------------
; Таблица вычисляемого перехода под надпись ЫКЛЮЧЕНА.
;----------------------------------------------------
addwf PCL,F ; PCL + W = ...
PC_ADR2 dt 0x00,0xFE,0x88,0x88,0x70,0x00,0xFE,0x00 ; символ Ы
dt 0x00,0x00,0xFE,0x10,0x28,0x44,0x82,0x00 ; символ К
dt 0x00,0x40,0x82,0x7E,0x02,0x02,0xFE,0x00 ; символ Л
dt 0x00,0xFE,0x10,0x7C,0x82,0x82,0x7C,0x00 ; символ Ю
dt 0x00,0x00,0x0E,0x10,0x10,0x10,0xFE,0x00 ; символ Ч
dt 0x00,0x00,0xFE,0x92,0x92,0x92,0x82,0x00 ; символ Е
dt 0x00,0x00,0xFE,0x10,0x10,0x10,0xFE,0x00 ; символ Н
dt 0x00,0x00,0xFC,0x22,0x22,0x22,0xFC,0x00 ; символ А
;-----------------------------------------------------------------------------------------------
; ................................ ; Символизирует
; ................................ : часть программы.
;********************************************************************************************
; Начало программы.
;********************************************************************************************
START bsf STATUS,RP0 ; Переход в 1-й банк. <-- ТОЧКА ОСТАНОВКИ
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Переход в 0-й банк.
;==================================================================================================
; Процедура вывода на индикацию надписи ЗАЩИТА ВЫКЛЮЧЕНА.
; Например, выводим эту надпись в 1-ю дисплейную строку.
;==================================================================================================
; Вывод на индикацию, в 1-ю строку 1-го кристалла, надписи ЗАЩИТА В.
;-------------------------------------------------------------------
nop ; Символизирует управление графическим модулем: выбор 1-го
; (левого) кристалла, выбор 1-й страницы, выбор 1-го столбца.
movlw .64 ; Запись, в регистр Count, количества
movwf Count ; выводимых, в “кристальную” строку, столбцов.
SNOVA_1 movf Count,W ; Count -> W.
sublw .64 ; .64 - Count = ... (Результат -> W).
movwf Reg ; W -> Reg.
call TABLE_1 ; Вызов ПП TABLE_1.
;----> Возврат по стеку из ПП TABLE (“добытое лежит” в W).
movwf PORTB ; Подготовка к выводу на индикацию текущего столбца
; (предустановка кода столбца на 8-ми линиях данных
; графического модуля).
nop ; Символизирует управление графическим модулем: обеспечение
; условия работы с байтами данных, стробирование, (т.е. вывод на
; индикацию), обеспечение условия работы с командами.
decfsz Count,F ; На индикацию выведены все столбцы или не все ?
goto SNOVA_1 ; Если не все, то выводим на индикацию следующий столбец.
; Если все, то окончание вывода на индикацию надписи ЗАЩИТА В
; и переход на вывод на индикацию надписи ЫКЛЮЧЕНА.
;-------------------------------------------------------------------
; Вывод на индикацию, в 1-ю строку 2-го кристалла, надписи ЫКЛЮЧЕНА.
;-------------------------------------------------------------------
nop ; Символизирует управление графическим модулем: выбор 2-го
; (правого) кристалла,выбор 1-й страницы, выбор 1-го столбца.
movlw .64 ;
movwf Count ;
SNOVA_2 movf Count,W ;
sublw .64 ; Аналогично,
movwf Reg ; только с вызовом
call TABLE_2 ; ПП TABLE_2.
movwf PORTB ;
nop ;
decfsz Count,F ;
goto SNOVA_2 ;

39
;------------------------------------
goto $ ; “Мертвяк” (если нужна многоцикличность,
; то, например, goto START).
;***************************************************************************************************
end ; Конец программы.

С учётом ранее сказанного, ничего принципиально нового, в этой программе, нет, за


исключением специфики управления графическим модулем и того, что надпись
выводится на индикацию в 2 приёма.
В данных случаях, “межблочная граница проходит” только через верхнюю таблицу
вычисляемого перехода, а через нижнюю таблицу не “проходит”, но этого вполне
достаточно для того чтобы убедиться в “безглюковой” работе (если эта “граница пройдёт” через
нижнюю таблицу вычисляемого перехода , то работа будет такой же “безглюковой”).

Вариант универсальной подпрограммы вычисляемого перехода с


идентификатором конца таблицы
Программная реализация этого программного варианта выглядит так:

Программа №9 (для графического модуля на основе м/к KS0108)


;***************************************************************************************************
; Программа, позволяющая проверить работу ПП универсального вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; “Прыжковый” регистр.
endc ; Конец блока.
;-----------------------------------------------------------------------------------------------
; “Межблочная граница” проходит через таблицу вычисляемого перехода (см. окно Program Memory),
; но при желании, её можно адресно сместить. При проверке, должна работать только одна из
; “нижележащих” директив org ... , а остальные должны быть заблокированы.
;-----------------------------------------------------------------------------------------------
; Через таблицу вычисляемого перехода TABLE_1 проходит
; “межблочная граница” 0-го и 1-го блоков.
;---------------------------------------------
org 0F2h ; Текст программы адресно смещён так, что верхняя часть ПП
; TABLE_1 “лежит” в блоке 0, а нижняя её часть, в блоке 1.
;---------------------------------------------
; Через таблицу вычисляемого перехода TABLE_1 проходит
; “межблочная граница” 1-го и 2-го блоков.
;---------------------------------------------
;;; org 1F2h ; Текст программы адресно смещён так, что верхняя часть ПП
;;; ; TABLE_1 “лежит” в блоке 1, а нижняя её часть, в блоке 2.
;---------------------------------------------
; Через таблицу вычисляемого перехода TABLE_1 проходит
; “межблочная граница” 2-го и 3-го блоков.
;---------------------------------------------
;;; org 2F2h ; Текст программы PC-смещён так, что верхняя часть ПП
;;; ; TABLE_1 “лежит” в блоке 2, а нижняя её часть, в блоке 3.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

; ................................ ; Часть программы.


;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Вызываемые ПП преобразования двоичного кода в код столбцов символов, выводимых на индикацию в
; дисплей графического модуля. Линии данных графического модуля подключены к выводам порта В.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; На индикацию, в строку дисплея, в 2 приёма (так как строка дисплея состоит из двух “однономерных”
; строк кристаллов), выводится надпись ЗАЩИТА ВЫКЛЮЧЕНА .
;====================================================================================================
; Для надписи ЗАЩИТА В .
;====================================================================================================
TABLE_1 movlw high PC_ADR1; Установка (через W), в PCH (посредством PCLATH), числового
movwf PCLATH ; значения старшего байта адреса той команды, которая помечена
; меткой PC_ADR1.
;---------------------------------------------------
; Будем “прыгать” в текущий PC-блок или в следующий?
;---------------------------------------------------
movf Reg,W ; “Прыжковое” число копируется в W.

40
addlw low PC_ADR1 ; Суммирование "прыжкового" числа и числового значения младшего
; байта адреса той команды, которая помечена меткой PC_ADR1.
btfsc STATUS,C ; Перенос был или нет?
incf PCLATH,F ; Если перенос был (соответствует работе в следующем PC-блоке),
; то PCLATH+1=... (то есть, PCH+1=...).
; Если переноса не было (соответствует работе в текущем
; PC-блоке), то команда инкремента не исполняется.
movf Reg,W ; “Прыжковое” число копируется в W.
;----------------------------------------------------
; Таблица вычисляемого перехода под надпись ЗАЩИТА В.
;----------------------------------------------------
addwf PCL,F ; PCL + W = ...
PC_ADR1 dt 0x00,0x00,0x44,0x82,0x92,0x92,0x6C,0x00 ; символ З
dt 0x00,0x00,0xFC,0x22,0x22,0x22,0xFC,0x00 ; символ А
dt 0x00,0x00,0x7E,0x40,0x7E,0x40,0x7E,0xC0 ; символ Щ
dt 0x00,0x00,0xFE,0x20,0x10,0x08,0xFE,0x00 ; символ И
dt 0x00,0x00,0x02,0x02,0xFE,0x02,0x02,0x00 ; символ Т
dt 0x00,0x00,0xFC,0x22,0x22,0x22,0xFC,0x00 ; символ А
dt 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; символ пусто (пробел)
dt 0x00,0x00,0xFE,0x92,0x92,0x92,0x6C,0x00,0xFF ; символ В и конец таблицы
;====================================================================================================
; Для надписи ЫКЛЮЧЕНА .
;====================================================================================================
TABLE_2 movlw high PC_ADR2;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR2 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;
;----------------------------------------------------
; Таблица вычисляемого перехода под надпись ЫКЛЮЧЕНА.
;----------------------------------------------------
addwf PCL,F ; PCL + W = ...
PC_ADR2 dt 0x00,0xFE,0x88,0x88,0x70,0x00,0xFE,0x00 ; символ Ы
dt 0x00,0x00,0xFE,0x10,0x28,0x44,0x82,0x00 ; символ К
dt 0x00,0x40,0x82,0x7E,0x02,0x02,0xFE,0x00 ; символ Л
dt 0x00,0xFE,0x10,0x7C,0x82,0x82,0x7C,0x00 ; символ Ю
dt 0x00,0x00,0x0E,0x10,0x10,0x10,0xFE,0x00 ; символ Ч
dt 0x00,0x00,0xFE,0x92,0x92,0x92,0x82,0x00 ; символ Е
dt 0x00,0x00,0xFE,0x10,0x10,0x10,0xFE,0x00 ; символ Н
dt 0x00,0x00,0xFC,0x22,0x22,0x22,0xFC,0x00,0xFF ; символ А и конец таблицы
;-----------------------------------------------------------------------------------------------
; ................................ ; Символизирует
; ................................ : часть программы.
;********************************************************************************************
; Начало программы.
;********************************************************************************************
START bsf STATUS,RP0 ; Переход в 1-й банк. <-- ТОЧКА ОСТАНОВКИ
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Переход в 0-й банк.
;==================================================================================================
; Процедура вывода на индикацию надписи ЗАЩИТА ВЫКЛЮЧЕНА.
; Например, выводим эту надпись в 1-ю дисплейную строку.
;==================================================================================================
; Вывод на индикацию, в 1-ю строку 1-го кристалла, надписи ЗАЩИТА В.
;-------------------------------------------------------------------
nop ; Символизирует управление графическим модулем: выбор 1-го
; (левого) кристалла, выбор 1-й страницы, выбор 1-го столбца.
clrf Reg ; Начинаем с верхней строки таблицы вычисляемого перехода.
SNOVA_1 call TABLE_1 ; Вызов ПП TABLE_1.
; --> Возврат по стеку из ПП TABLE (“добытое лежит” в W).
movwf PORTB ; Подготовка к выводу на индикацию текущего столбца
; (предустановка кода столбца на 8-ми линиях данных
; графического модуля).
sublw .255 ; .255 – W = ...
btfsc STATUS,Z ; Это .255 или другое число ?
goto VIHOD_1 ; Если .255, то окончание вывода на индикацию надписи ЗАЩИТА В
; и переход на вывод на индикацию надписи ЫКЛЮЧЕНА.
; Если другое число, то далее
; (продолжение вывода надписи на индикацию).
nop ; Символизирует управление графическим модулем: обеспечение
; условия работы с байтами данных, стробирование, (т.е. вывод на
; индикацию), обеспечение условия работы с командами.
incf Reg,F ; Reg + 1 = ... Результат -> Reg (подготовка к “прыжку”
; в следующую строку таблицы вычисляемого перехода).
goto SNOVA_1 ; Продолжаем перебор далее.
;-------------------------------------------------------------------
; Вывод на индикацию, в 1-ю строку 2-го кристалла, надписи ЫКЛЮЧЕНА.
;-------------------------------------------------------------------
VIHOD_1 nop ; Символизирует управление графическим модулем: выбор 2-го
; (правого) кристалла, выбор 1-й страницы, выбор 1-го столбца.

41
clrf Reg ;
SNOVA_2 call TABLE_2 ;
movwf PORTB ; Аналогично,
sublw .255 ; только с вызовом
btfsc STATUS,Z ; ПП TABLE_2.
goto VIHOD_2 ; Окончание вывода на индикацию надписи и и переход туда,
; куда нужно (выход из процедуры). Если нужна многоцикличность,
; то, например, goto START.
nop ;
incf Reg,F ;
goto SNOVA_2 ;
;------------------------------------
VIHOD_2 goto $ ; В данном, примитивном случае, “мертвяк”,
; но в реале – продолжение исполнения программы.
;***************************************************************************************************
end ; Конец программы.

По аналогии с программой №6, в программе №9, число проходов не назначается,


и верхняя таблица вычисляемого перехода будет последовательно отрабатываться до
тех пор, пока рабочая точка программы не “споткнётся” о признак конца таблицы (0xFF),
что означает окончание вывода на индикацию части надписи (той, которая “дислоцируется” в
заданной строке 1-го кристалла. Она отрабатывается первой).
После этого, по такому же принципу, на индикацию будет выведена остальная часть
надписи (та, которая “дислоцируется” в заданной строке 2-го кристалла).
После этого à выход из процедуры.

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


порядку формирования “прыжковых” чисел (для графического модуля на основе м/к KS0108).

“Фиксированные” надписи это конечно же необходимая “штуковина”, но если речь идёт


о воистину “орлином взоре”, направленном в “потаённые уголки” предмета данного
разговора, то как быть с “динамическими анархистами”, изменяющиеся результаты
“деятельности” которых нужно, тем или иным способом, периодически отслеживать?
Например, как быть с результатами измерения “гуляющей” частоты, количества
посетителей гипермаркета, количества неорганизованных попугаев, пролетающих мимо
попугайного датчика (условно. Также как и электровеник, и заяц с лыжами, и пикирующее парнокопытное, и …),
и т.д. (это широчайший класс задач)?
Ответ: необходимо “ваяние” соответствующих (“адаптированных” под эту “анархию”) программных
процедур, без осознания принципов “ваяния” которых, в этом “анарходинамическом”
деле, никак не обойтись.

Примечание: так как эта статья посвящена вычисляемым переходам, то следует отметить, что в
приложении к случаям произвольного порядка формирования “прыжковых” чисел, ЖК-модули как бы
“выпадают” из её контекста.
Мотивация: в ЖК-модулях, таблица знакогенератора организована аппаратно.
Это означает то, что в приложении к ЖК-модулям, применение вычисляемых переходов целесообразно
только тогда, когда порядок формирования “прыжковых” чисел строго задан (например,
программы №4, 5, 6), а также и то, что в рассматриваемом, “динамоанархическом” случае, востребованы
не вычисляемые переходы, а косвенная адресация. Это отдельный разговор, “привязанный” к
обсуждению деталей того, что связано с оперпамятью.

С учётом “вышележащего” примечания, речь пойдёт о соответствующих программных


решениях, применимых к графическим модулям на основе м/к KS0108.
“Сермяжная суть” предмета “нижележащего толковища” состоит в том, что в
графических модулях нет аппаратно организованной таблицы знакогенератора.
Значит, её нужно организовать программно.
Если упомянуто слово “таблица”, то в данном случае, это подразумевает применение
вычисляемых переходов, и не одного, а поболее (в подавляющем большинстве случаев, таблица
состоит из нескольких строк).
“Привяжусь” к выводу на индикацию “анархических” (если “анархопрограммно” изменять
предустановки LED7 … LED0) результатов неких замеров “того-сего”, визуально оформленных в
виде “набора” символов чисел (в данном случае, “набор” такой: 12345678. Это 8 десятичных разрядов.
Символы “выстроены” по привычному, для всех, “ранжиру”), причём, содержимое этого “набора”, при

42
желании, можно задать программно (а в реале, это результаты измерения/подсчёта “того-сего”, причём, в
динамике, но это отдельный разговор).
С целью обеспечения относительного единообразия оформления текстов программ,
тексты “нижележащих” программ “ваялись по образу и подобию вышележащих”
(естественно, что различия, в деталях, есть, но “сермяжная, вычисляемопереходная суть” одна и та же) .
8-столбцовые коды символов цифр 0 … 9 (в данном случае, одно знакоместо состоит из 8-ми столбцов)
формируются с помощью прилагаемого, к этой статье, CHR-файла библиотеки русских
символов (а можно и английских. Цифры везде одинаковы).
Сначала, предлагаю Вашему вниманию “безуниверсальный” (без применения операторов high и
low) вариант, в котором можно отследить как “безбяковую”, так и “бяковую” (в смысле
наличия “глюков”) работу (если Вы желаете стать МАСТЕРОМ, то “сермяжную суть PC-глюков”, а также и
других разновидностей “глюков”, нужно осознать лично !!! Понимаю, что хлопотно, но куда деваться?):

Программа №10 (для графического модуля на основе м/к KS0108)


;***************************************************************************************************
; Программа, позволяющая проверить работу вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; “Прыжковый” регистр.
Mem ; Регистр хранения содержимого текущего LEDа
Count ; Счётчик проходов.
;--------------------------
; Вспомогательные регистры:
;--------------------------
LED7 ; ----“----
LED6 ; ----“----
LED5 ; ----“----
LED4 ; Регистры результата
LED3 ; 2_10 преобразования.
LED2 ; ----“----
LED1 ; ----“----
LED0 ; ----“----
Flag ; Регистр флагов (для контроля событий
; вывода символов на индикацию).
;--------------------------
endc ; Конец блока.
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; ВНИМАНИЕ! ПРИ ПРОВЕРКЕ, ДОЛЖНА РАБОТАТЬ ТОЛЬКО ОДНА ИЗ “НИЖЕЛЕЖАЩИХ” ДИРЕКТИВ org ...
; ОСТАЛЬНЫЕ ДОЛЖНЫ БЫТЬ ЗАБЛОКИРОВАНЫ + ВЫПОЛНЕНИЕ “НИЖЕЛЕЖАЩЕЙ” ...!!!! ИНСТРУКЦИИ !!!...
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; Варианты “безбяковой” работы.
; Можно назначить и другие PC-адреса, лишь-бы не выйти за “границы” блока.
;-----------------------------------------------------------------------------------------------
;;; org 0 ; Первая команда программы “дислоцируется” в блоке 0.
;;; org 100h ; Первая команда программы “дислоцируется” в блоке 1.
;;; org 200h ; Первая команда программы “дислоцируется” в блоке 2.
;;; org 300h ; Первая команда программы “дислоцируется” в блоке 3.
;-----------------------------------------------------------------------------------------------
; Варианты “бяковой” работы: “межблочная граница” проходит через таблицу вычисляемого перехода
; (см. окно Program Memory), но при желании, её можно адресно сместить.
;-----------------------------------------------------------------------------------------------
; Через одну из таблиц вычисляемого перехода проходит
; “межблочная граница” 0-го и 1-го блоков.
;----------------------------------------------------
org 0EFh ; Текст программы адресно смещён так, что верхняя часть ПП
; “знакогенератора” символа “1” (SIMVOL_1) “лежит” в блоке 0,
; а нижняя её часть, в блоке 1.
; “Глюк” будет при переходе от 0x84 к 0xFE.
;----------------------------------------------------
; Через одну из таблиц вычисляемого перехода проходит
; “межблочная граница” 1-го и 2-го блоков.
;----------------------------------------------------
;;; org 1EFh ; Текст программы адресно смещён так, что верхняя часть ПП
;;; ; “знакогенератора” символа “1” (SIMVOL_1) “лежит” в блоке 1,
;;; ; а нижняя её часть, в блоке 2.
;;; ; “Глюк” будет при переходе от 0x84 к 0xFE.
;----------------------------------------------------
; Через одну из таблиц вычисляемого перехода проходит

43
; “межблочная граница” 2-го и 3-го блоков.
;----------------------------------------------------
;;; org 2EFh ; Текст программы PC-смещён так, что верхняя часть ПП
;;; ; “знакогенератора” символа “1” (SIMVOL_1) “лежит” в блоке 2,
;;; ; а нижняя её часть, в блоке 3.
;;; ; “Глюк” будет при переходе от 0x84 к 0xFE.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

; ................................ ; Часть программы.


;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Вызываемые ПП преобразования двоичного кода в код столбцов символов, выводимых на индикацию в
; дисплей графического модуля (программно организованный “знакогенератор”).
; Линии данных графического модуля подключены к выводам порта В.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Для символа "0".
;------------------------------------
SIMVOL_0 movf Reg,W ; Reg -> W
addwf PCL,F ; PCL + W = ...
dt 0x00,0x00,0x7C,0xA2,0x92,0x8A,0x7C,0x00 ; 8-столбцовый код символа “0”.
;------------------------------------
; Для символа "1".
;------------------------------------
SIMVOL_1 movf Reg,W ; Reg -> W
addwf PCL,F ; PCL + W = ...
dt 0x00,0x00,0x00,0x84,0xFE,0x80,0x00,0x00 ; 8-столбцовый код символа “1”.
;------------------------------------
; Для символа "2".
;------------------------------------
SIMVOL_2 movf Reg,W ; Reg -> W
addwf PCL,F ; PCL + W = ...
dt 0x00,0x00,0x84,0xC2,0xA2,0x92,0x8C,0x00 ; 8-столбцовый код символа “2”.
;------------------------------------
; Для символа "3".
;------------------------------------
SIMVOL_3 movf Reg,W ; Reg -> W
addwf PCL,F ; PCL + W = ...
dt 0x00,0x00,0x42,0x82,0x8A,0x96,0x62,0x00 ; 8-столбцовый код символа “3”.
;------------------------------------
; Для символа "4".
;------------------------------------
SIMVOL_4 movf Reg,W ; Reg -> W
addwf PCL,F ; PCL + W = ...
dt 0x00,0x00,0x30,0x28,0x24,0xFE,0x20,0x00 ; 8-столбцовый код символа “4”.
;------------------------------------
; Для символа "5".
;------------------------------------
SIMVOL_5 movf Reg,W ; Reg -> W
addwf PCL,F ; PCL + W = ...
dt 0x00,0x00,0x4E,0x8A,0x8A,0x8A,0x72,0x00 ; 8-столбцовый код символа “5”.
;------------------------------------
; Для символа "6".
;------------------------------------
SIMVOL_6 movf Reg,W ; Reg -> W
addwf PCL,F ; PCL + W = ...
dt 0x00,0x00,0x78,0x94,0x92,0x92,0x60,0x00 ; 8-столбцовый код символа “6”.
;------------------------------------
; Для символа "7".
;------------------------------------
SIMVOL_7 movf Reg,W ; Reg -> W
addwf PCL,F ; PCL + W = ...
dt 0x00,0x00,0x06,0x02,0xE2,0x12,0x0E,0x00 ; 8-столбцовый код символа “7”.
;------------------------------------
; Для символа "8".
;------------------------------------
SIMVOL_8 movf Reg,W ; Reg -> W
addwf PCL,F ; PCL + W = ...
dt 0x00,0x00,0x6C,0x92,0x92,0x92,0x6C,0x00 ; 8-столбцовый код символа “8”.
;------------------------------------
; Для символа "9".
;------------------------------------
SIMVOL_9 movf Reg,W ; Reg -> W
addwf PCL,F ; PCL + W = ...
dt 0x00,0x00,0x0C,0x92,0x92,0x52,0x3C,0x00 ; 8-столбцовый код символа “9”.
;-----------------------------------------------------
; Для символа "пробел" (под гашение незначащих нолей).
;-----------------------------------------------------
SIMVOL_NO movf Reg,W ; Reg -> W
addwf PCL,F ; PCL + W = ...
dt 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; 8-столбцовый код символа “пробел”.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

44
; ................................ ; Символизирует
; ................................ : часть программы.
;********************************************************************************************
; Начало программы.
;********************************************************************************************
START bsf STATUS,RP0 ; Переход в 1-й банк. <-- ТОЧКА ОСТАНОВКИ
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Переход в 0-й банк.
;===================================================================================================
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИНСТРУКЦИЯ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;===================================================================================================
; Если разблокировано org 0 или org 0EFh, то заблокировать всё то, что ниже выделено красным цветом.
;---------------------------------------------------------------------------------------------------
; Разблокировать, если разблокировано org 100h или org 1EFh
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 1 ; Так как “знакогенератор лежит”
;;; movwf PCLATH ; в блоке 1, то выбор блока 1.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 200h или org 2EFh
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 2 ; Так как “знакогенератор лежит”
;;; movwf PCLATH ; в блоке 2, то выбор блока 2.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 300h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 3 ; Так как “знакогенератор лежит”
;;; movwf PCLATH ; в блоке 3, то выбор блока 3.
;----------------------------------------------------------------------------------------------------
; Примечание: прежде чем выводить данные на индикацию, нужно, тем или иным способом их заиметь.
; Обычно, результат “оседает” в N-байтном регистре (в двоичном виде. В детали не вдаюсь).
; Далее, содержимое N-байтного регистра нужно “протащить” через соответствующую ПП 2_10
; преобразования (варианты “расписаны” в статье “ПП 2_10 преобразований”), но в данном случае,
; во избежание усложнения текста программы, результаты работы ПП BIN2_10 сымитированы (кстати,
; слово “сымитирован” пишется не через “и”, а через “ы”, а слово “имитация” пишется через “и”),
; например, так (можно и по-другому):
;----------------------------------------------------------------------------------------------------
; Имитация результатов работы ПП BIN2_10.
;----------------------------------------
movlw 1 ; В регистр LED7
movwf LED7 ; записано число 1, что соответствует символу “1”.
movlw 2 ; В регистр LED6
movwf LED6 ; записано число 2, что соответствует символу “2”.
movlw 3 ; В регистр LED5
movwf LED5 ; записано число 3, что соответствует символу “3”.
movlw 4 ; В регистр LED4
movwf LED4 ; записано число 4, что соответствует символу “4”.
movlw 5 ; В регистр LED3
movwf LED3 ; записано число 5, что соответствует символу “5”.
movlw 6 ; В регистр LED2
movwf LED2 ; записано число 6, что соответствует символу “6”.
movlw 7 ; В регистр LED1
movwf LED1 ; записано число 7, что соответствует символу “7”.
movlw 8 ; В регистр LED0
movwf LED0 ; записано число 8, что соответствует символу “8”.
;----------------------------------------------------------------------------------------------------
; В данном случае, результаты 2_10 преобразования “оседают” в регистрах LED7..LED0, что соответствует
; дальнейшему выводу на индикацию показания 12345678 (незначащих нолей нет).
;----------------------------------------------------------------------------------------------------
; Если это нужно, то в дальнейшем можно “протащить” результаты 2_10 преобразования через программную
; процедуру гашения незначащих нолей (можно с ней или без неё. Это зависит от желания конструктора).
; Принцип гашения прост: символ незначащего “0” программно заменяется на символ “пробел” (или
; “пусто”. Обычно, крайний правый ноль не гасится, но можно и загасить или сделать так, как хочется).
; Если, взамен любого из “вышележащих” чисел, “настукать” число .10, то можно отследить программный
; “механизм” вывода на индикацию символа “пробел”.
;----------------------------------------------------------------------------------------------------
clrf Flag ; Регистр флагов очищен (подготовка к “бит-регистрированию”
; событий вывода символов на индикацию).
;====================================================================================================
; Процедура вывода на индикацию, в дисплей графического модуля, символов, “привязанных” к числовым
; результатам предыдущей работы.
;====================================================================================================
; Подготовительные операции.
;------------------------------------
nop ; Символизирует управление графическим модулем: выбор кристалла,
; выбор страницы этого кристалла, выбор нужного столбца страницы,
; вывод инструкции “Set Address”, в данном случае, в порт В,
; строб под эту инструкцию.
;------------------------------------
; "Администраторская" группа команд.

45
;------------------------------------
movf LED7,W ; LED7 -> W.
call CIFRA ; Вызов ПП CIFRA.
;---> Возврат по стеку из ПП CIFRA
bsf Flag,7 ; Контроль окончания вывода на индикацию символа в 8-е,
; самое левое знакоместо (необязательная команда).
;------------------------------------
movf LED6,W ;
call CIFRA ; Аналогично, но для LED6.
bsf Flag,6 ; Контроль окончания вывода на индикацию символа в 7-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED5,W ;
call CIFRA ; Аналогично, но для LED5.
bsf Flag,5 ; Контроль окончания вывода на индикацию символа в 6-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED4,W ;
call CIFRA ; Аналогично, но для LED4.
bsf Flag,4 ; Контроль окончания вывода на индикацию символа в 5-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED3,W ;
call CIFRA ; Аналогично, но для LED3.
bsf Flag,3 ; Контроль окончания вывода на индикацию символа в 4-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED2,W ;
call CIFRA ; Аналогично, но для LED2.
bsf Flag,2 ; Контроль окончания вывода на индикацию символа в 3-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED1,W ;
call CIFRA ; Аналогично, но для LED1.
bsf Flag,1 ; Контроль окончания вывода на индикацию символа во 2-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED0,W ;
call CIFRA ; Аналогично, но для LED0.
bsf Flag,0 ; Контроль окончания вывода на индикацию символа в 1-е,
; самое правое знакоместо (необязательная команда).
;------------------------------------------------
; Окончание процедуры вывода данных на индикацию.
;------------------------------------------------
goto $ ; В данном, учебно-тренировочном случае, “мертвяк”,
; а в реале, переход туда, куда нужно
; (с целью продолжения исполнения программы).
;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
; Группа подпрограмм вывода на индикацию символов цифр.
;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
; Подготовительные операции.
;------------------------------------
CIFRA movwf Mem ; Сохранение, в регистре Mem, содержимого текущего LEDа.
movlw .8 ; В регистр Count записывается
movwf Count ; число проходов.
;====================================================================================================
; Циклическая ПП вывода на индикацию символов цифр.
;====================================================================================================
; В любой из таблиц вычисляемых переходов, начинаем выводить на индикацию символ, начиная от крайнего
; левого столбца и заканчивая крайним правым (в данном случае, знакоместо состоит из 8-ми столбцов).
;----------------------------------------------------------------------------------------------------
SNOVA movf Count,W ; Count -> W.
sublw .8 ; .8 - Count = ... (результат -> в W).
movwf Reg ; W -> Reg ("прыжковое" число копируется в Reg).
;-------------------------------------
; Для символа "0".
;-------------------------------------
movf Mem,W ; Mem -> W
sublw 0 ; 0 - W = ... (это символ “0” или другой ?)
btfss STATUS,Z ; Результат =0 или не=0 (нужно выводить на
; индикацию символ "0" или не нужно)?
goto $+3 ; Если не=0, то ПП "знакогенератора" символа "0"
; не вызывается (переход в следующую проверку).
call SIMVOL_0 ; Если =0, то вызывается ПП "знакогенератора" символа "0"
;-> Возврат по стеку из ПП SIMVOL_0 ; (после возврата, код текущего столбца “лежит” в W).
goto OBHOD ; Обход остальных проверок (переход в концовку процедуры).
;--------------------------------------------------------------------------------------------
; Примечание: в приложении к ПИКам базового и среднего семейства, $+3 соответствует “прыжку”,
; через 2 команды, на 3-ю.
;--------------------------------------------------------------------------------------------
; Для символа "1".
;-------------------------------------

46
movf Mem,W ;
sublw 1 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_1 ; Переход в "знакогенератор" символа "1".
goto OBHOD ;
;-------------------------------------
; Для символа "2".
;-------------------------------------
movf Mem,W ;
sublw 2 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_2 ; Переход в "знакогенератор" символа "2".
goto OBHOD ;
;-------------------------------------
; Для символа "3".
;-------------------------------------
movf Mem,W ;
sublw 3 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_3 ; Переход в "знакогенератор" символа "3".
goto OBHOD ;
;-------------------------------------
; Для символа "4".
;-------------------------------------
movf Mem,W ;
sublw 4 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_4 ; Переход в "знакогенератор" символа "4".
goto OBHOD ;
;-------------------------------------
; Для символа "5".
;-------------------------------------
movf Mem,W ;
sublw 5 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_5 ; Переход в "знакогенератор" символа "5".
goto OBHOD ;
;-------------------------------------
; Для символа "6".
;-------------------------------------
movf Mem,W ;
sublw 6 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_6 ; Переход в "знакогенератор" символа "6".
goto OBHOD ;
;-------------------------------------
; Для символа "7".
;-------------------------------------
movf Mem,W ;
sublw 7 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_7 ; Переход в "знакогенератор" символа "7".
goto OBHOD ;
;-------------------------------------
; Для символа "8".
;-------------------------------------
movf Mem,W ;
sublw 8 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_8 ; Переход в "знакогенератор" символа "8".
goto OBHOD ;
;-------------------------------------
; Для символа "9".
;-------------------------------------
movf Mem,W ;
sublw 9 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_9 ; Переход в "знакогенератор" символа "9".
goto OBHOD ;
;-----------------------------------------------------
; Для символа "пробел" (под гашение незначащих нолей).
;-----------------------------------------------------
movf Mem,W ;

47
sublw .10 ; Аналогично.
btfss STATUS,Z ;
return ; Если проверяемое число более .10 (символ не подлежит
; отображению), то вывод на индикацию программно блокируется
; (путём возврата из ПП CIFRA).
call SIMVOL_NO ; Переход в "знакогенератор" символа "пробел".
;-------------------------------------
; Концовка.
;-------------------------------------
OBHOD movwf PORTB ; Код текущего столбца копируется в регистр PORTB.
nop ; Символизирует управление графическим модулем: обеспечение
; условия работы с байтами данных, стробирование, (т.е. вывод на
; индикацию), обеспечение условия работы с командами.
decfsz Count,F ; Count - 1 = ...
goto SNOVA ; Если результат не=0, то переход на начало вывода
; на индикацию следующего столбца.
return ; Если результат =0 (т.е. текущий символ выведен на индикацию),
; то возврат по стеку в то "место", из которого произошел вызов
; ПП CIFRA (переход на работу со следующим символом, а если
; текущий символ является последним, то выход из процедуры).
;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
; ................................ ; Символизирует
; ................................ : часть программы.
;***************************************************************************************************
end ; Конец программы.

“Влётные” данные, подлежащие отображению, “лежат” (сымитированы) в LED7 … LED0.


По причине отсутствия, в графических модулях, аппаратного знакогенератора,
организован программный знакогенератор (текст выделен тёмно-красным цветом).
В данном случае, это 11-позиционный знакогенератор (10 символов цифр + символ “пробел”),
но это не догма (в зависимости от конкретики решаемой задачи, количество позиций может быть и иным).

Reg à “прыжковый” регистр (по аналогии с предыдущими программами) .

Mem à регистр хранения содержимого текущего LEDа.


В зависимости от числового значения его содержимого, из группы таблиц
вычисляемых переходов, выбирается одна из таблиц (та, в которой “лежит” 8-столбцовый код
символа, поставленный в соответствие числу, “лежащему” в текущем LEDе).

Count à регистр 8-циклового счётчика проходов, который (счётчик), в “промежутках”


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

Flag à вспомогательный регистр флагов, который исполняет исключительно


контрольную (вспомогательную) функцию.
Изначально, он “принудительно обнулён”, а в процессе дальнейшей работы, по мере
заполнения знакомест, в его бит-флагах, последовательно (по окончании вывода на индикацию
каждого текущего символа) устанавливаются единицы (по направлению от бита №7 к биту №0).
Если такой “сервис” не нужен, то этот регистр (а также и то, что связано с обращениями к нему)
можно убрать из текста программы.

Символы цифр выводятся на индикацию последовательно (один за другим), от начала


заданной строки кристалла (в строке кристалла “дислоцируется” 8 знакомест по 8 столбцов в каждом
знакоместе) и по направлению слева направо (1à 2 à 3 à…à 8).
По причине того, что заполнение одного знакоместа (то есть, формирование “графомодульной
картинки” одного символа) происходит за 8 “байт-приёмов”, отслеживание (в симуляторе) работы
программы не так уж и комфортно, но зная что к чему (плюс, терпение/”упёртость”/…),
отследить вполне можно/нужно, и не ради того, что я настаиваю на этом, а в Ваших
интересах (естественно, если Ваши намерения серьёзны).
Программа, в состав которой входит группа универсальных ПП вычисляемого
перехода, в приложении к произвольному порядку формирования “прыжковых” чисел,
выглядит так:

48
Программа №11 (для графического модуля на основе м/к KS0108)
;***************************************************************************************************
; Программа, позволяющая проверить работу вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; “Прыжковый” регистр.
Mem ; Регистр хранения содержимого текущего LEDx
Count ; Счётчик проходов.
;--------------------------
; Вспомогательные регистры:
;--------------------------
LED7 ; ----“----
LED6 ; ----“----
LED5 ; ----“----
LED4 ; Регистры результата
LED3 ; 2_10 преобразования.
LED2 ; ----“----
LED1 ; ----“----
LED0 ; ----“----
Flag ; Регистр флагов (для контроля событий
; вывода символов на индикацию).
;--------------------------
endc ; Конец блока.
;-----------------------------------------------------------------------------------------------
; “Межблочная граница” проходит через таблицу вычисляемого перехода (см. окно Program Memory),
; но при желании, её можно адресно сместить. При проверке, должна работать только одна из
; “нижележащих” директив org ... , а остальные должны быть заблокированы.
;-----------------------------------------------------------------------------------------------
; Через одну из таблиц вычисляемого перехода проходит
; “межблочная граница” 0-го и 1-го блоков.
;----------------------------------------------------
org 0E3h ; Текст программы адресно смещён так, что верхняя часть ПП
; “знакогенератора” символа “1” (SIMVOL_1) “лежит” в блоке 0,
; а нижняя её часть, в блоке 1.
; “Глюк” будет при переходе от 0x84 к 0xFE.
;----------------------------------------------------
; Через одну из таблиц вычисляемого перехода проходит
; “межблочная граница” 1-го и 2-го блоков.
;----------------------------------------------------
;;; org 1E3h ; Текст программы адресно смещён так, что верхняя часть ПП
;;; ; “знакогенератора” символа “1” (SIMVOL_1) “лежит” в блоке 1,
;;; ; а нижняя её часть, в блоке 2.
;;; ; “Глюк” будет при переходе от 0x84 к 0xFE.
;----------------------------------------------------
; Через одну из таблиц вычисляемого перехода проходит
; “межблочная граница” 2-го и 3-го блоков.
;----------------------------------------------------
;;; org 2E3h ; Текст программы PC-смещён так, что верхняя часть ПП
;;; ; “знакогенератора” символа “1” (SIMVOL_1) “лежит” в блоке 2,
;;; ; а нижняя её часть, в блоке 3.
;;; ; “Глюк” будет при переходе от 0x84 к 0xFE.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

; ................................ ; Часть программы.


;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Вызываемые ПП преобразования двоичного кода в код столбцов символов, выводимых на индикацию в
; дисплей графического модуля (программно организованный “знакогенератор”).
; Линии данных графического модуля подключены к выводам порта В.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Для символа "0".
;------------------------------------
SIMVOL_0 movlw high PC_ADR0; Установка (через W), в PCH (посредством PCLATH), числового
movwf PCLATH ; значения старшего байта адреса той команды, которая помечена
; меткой PC_ADR0.
;---------------------------------------------------
; Будем “прыгать” в текущий PC-блок или в следующий?
;---------------------------------------------------
movf Reg,W ; “Прыжковое” число копируется в W.
addlw low PC_ADR0 ; Суммирование "прыжкового" числа и числового значения младшего
; байта адреса той команды, которая помечена меткой PC_ADR0.
btfsc STATUS,C ; Перенос был или нет?

49
incf PCLATH,F ; Если перенос был (соответствует работе в следующем PC-блоке),
; то PCLATH+1=... (то есть, PCH+1=...).
; Если переноса не было (соответствует работе в текущем
; PC-блоке), то команда инкремента не исполняется.
movf Reg,W ; “Прыжковое” число копируется в W.

addwf PCL,F ; PCL + W = ...


PC_ADR0 dt 0x00,0x00,0x7C,0xA2,0x92,0x8A,0x7C,0x00 ; 8-столбцовый код символа “0”.
;------------------------------------
; Для символа "1".
;------------------------------------
SIMVOL_1 movlw high PC_ADR1;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR1 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR1 dt 0x00,0x00,0x00,0x84,0xFE,0x80,0x00,0x00 ; 8-столбцовый код символа “1”.
;------------------------------------
; Для символа "2".
;------------------------------------
SIMVOL_2 movlw high PC_ADR2;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR2 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR2 dt 0x00,0x00,0x84,0xC2,0xA2,0x92,0x8C,0x00 ; 8-столбцовый код символа “2”.
;------------------------------------
; Для символа "3".
;------------------------------------
SIMVOL_3 movlw high PC_ADR3;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR3 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR3 dt 0x00,0x00,0x42,0x82,0x8A,0x96,0x62,0x00 ; 8-столбцовый код символа “3”.
;------------------------------------
; Для символа "4".
;------------------------------------
SIMVOL_4 movlw high PC_ADR4;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR4 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR4 dt 0x00,0x00,0x30,0x28,0x24,0xFE,0x20,0x00 ; 8-столбцовый код символа “4”.
;------------------------------------
; Для символа "5".
;------------------------------------
SIMVOL_5 movlw high PC_ADR5;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR5 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR5 dt 0x00,0x00,0x4E,0x8A,0x8A,0x8A,0x72,0x00 ; 8-столбцовый код символа “5”.
;------------------------------------
; Для символа "6".
;------------------------------------
SIMVOL_6 movlw high PC_ADR6;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR6 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.

50
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR6 dt 0x00,0x00,0x78,0x94,0x92,0x92,0x60,0x00 ; 8-столбцовый код символа “6”.
;------------------------------------
; Для символа "7".
;------------------------------------
SIMVOL_7 movlw high PC_ADR7;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR7 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR7 dt 0x00,0x00,0x06,0x02,0xE2,0x12,0x0E,0x00 ; 8-столбцовый код символа “7”.
;------------------------------------
; Для символа "8".
;------------------------------------
SIMVOL_8 movlw high PC_ADR8;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR8 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR8 dt 0x00,0x00,0x6C,0x92,0x92,0x92,0x6C,0x00 ; 8-столбцовый код символа “8”.
;------------------------------------
; Для символа "9".
;------------------------------------
SIMVOL_9 movlw high PC_ADR9;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR9 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR9 dt 0x00,0x00,0x0C,0x92,0x92,0x52,0x3C,0x00 ; 8-столбцовый код символа “9”.
;-----------------------------------------------------
; Для символа "пробел" (под гашение незначащих нолей).
;-----------------------------------------------------
SIMVOL_NO movlw high PC_ADRNO;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADRNO ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADRNO dt 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; 8-столбцовый код символа “пробел”.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; ................................ ; Символизирует
; ................................ : часть программы.
;********************************************************************************************
; Начало программы.
;********************************************************************************************
START bsf STATUS,RP0 ; Переход в 1-й банк. <-- ТОЧКА ОСТАНОВКИ
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Переход в 0-й банк.
;----------------------------------------------------------------------------------------------------
; Примечание: прежде чем выводить данные на индикацию, нужно, тем или иным способом их заиметь.
; Обычно, результат “оседает” в N-байтном регистре (в двоичном виде. В детали не вдаюсь).
; Далее, содержимое N-байтного регистра нужно “протащить” через соответствующую ПП 2_10
; преобразования (варианты “расписаны” в статье “ПП 2_10 преобразований”), но в данном случае,
; во избежание усложнения текста программы, результаты работы ПП BIN2_10 сымитированы (кстати,
; слово “сымитирован” пишется не через “и”, а через “ы”, а слово “имитация” пишется через “и”),
; например, так (можно и по-другому):
;----------------------------------------------------------------------------------------------------
; Имитация результатов работы ПП BIN2_10.
;----------------------------------------
movlw 1 ; В регистр LED7
movwf LED7 ; записано число 1, что соответствует символу “1”.
movlw 2 ; В регистр LED6
movwf LED6 ; записано число 2, что соответствует символу “2”.
movlw 3 ; В регистр LED5
movwf LED5 ; записано число 3, что соответствует символу “3”.

51
movlw 4 ; В регистр LED4
movwf LED4 ; записано число 4, что соответствует символу “4”.
movlw 5 ; В регистр LED3
movwf LED3 ; записано число 5, что соответствует символу “5”.
movlw 6 ; В регистр LED2
movwf LED2 ; записано число 6, что соответствует символу “6”.
movlw 7 ; В регистр LED1
movwf LED1 ; записано число 7, что соответствует символу “7”.
movlw 8 ; В регистр LED0
movwf LED0 ; записано число 8, что соответствует символу “8”.
;----------------------------------------------------------------------------------------------------
; В данном случае, результаты 2_10 преобразования “оседают” в регистрах LED7..LED0, что соответствует
; дальнейшему выводу на индикацию показания 12345678 (незначащих нолей нет).
;----------------------------------------------------------------------------------------------------
; Если это нужно, то в дальнейшем можно “протащить” результаты 2_10 преобразования через программную
; процедуру гашения незначащих нолей (можно с ней или без неё. Это зависит от желания конструктора).
; Принцип гашения прост: символ незначащего “0” программно заменяется на символ “пробел” (или
; “пусто”. Обычно, крайний правый ноль не гасится, но можно и загасить или сделать так, как хочется).
; Если, взамен любого из “вышележащих” чисел, “настукать” число .10, то можно отследить программный
; “механизм” вывода на индикацию символа “пробел”.
;----------------------------------------------------------------------------------------------------
clrf Flag ; Регистр флагов очищен (подготовка к “бит-регистрированию”
; событий вывода символов на индикацию).
;====================================================================================================
; Процедура вывода на индикацию, в дисплей графического модуля, символов, “привязанных” к числовым
; результатам предыдущей работы.
;====================================================================================================
; Подготовительные операции.
;------------------------------------
nop ; Символизирует управление графическим модулем: выбор кристалла,
; выбор страницы этого кристалла, выбор нужного столбца страницы,
; вывод инструкции “Set Address”, в данном случае, в порт В,
; строб под эту инструкцию.
;------------------------------------
; "Администраторская" группа команд.
;------------------------------------
movf LED7,W ; LED7 -> W.
call CIFRA ; Вызов ПП CIFRA.
;---> Возврат по стеку из ПП CIFRA
bsf Flag,7 ; Контроль окончания вывода на индикацию символа в 8-е,
; самое левое знакоместо (необязательная команда).
;------------------------------------
movf LED6,W ;
call CIFRA ; Аналогично, но для LED6.
bsf Flag,6 ; Контроль окончания вывода на индикацию символа в 7-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED5,W ;
call CIFRA ; Аналогично, но для LED5.
bsf Flag,5 ; Контроль окончания вывода на индикацию символа в 6-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED4,W ;
call CIFRA ; Аналогично, но для LED4.
bsf Flag,4 ; Контроль окончания вывода на индикацию символа в 5-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED3,W ;
call CIFRA ; Аналогично, но для LED3.
bsf Flag,3 ; Контроль окончания вывода на индикацию символа в 4-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED2,W ;
call CIFRA ; Аналогично, но для LED2.
bsf Flag,2 ; Контроль окончания вывода на индикацию символа в 3-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED1,W ;
call CIFRA ; Аналогично, но для LED1.
bsf Flag,1 ; Контроль окончания вывода на индикацию символа во 2-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED0,W ;
call CIFRA ; Аналогично, но для LED0.
bsf Flag,0 ; Контроль окончания вывода на индикацию символа в 1-е,
; самое правое знакоместо (необязательная команда).
;------------------------------------------------
; Окончание процедуры вывода данных на индикацию.
;------------------------------------------------
goto $ ; В данном, учебно-тренировочном случае, “мертвяк”,
; а в реале, переход туда, куда нужно
; (с целью продолжения исполнения программы).

52
;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
; Группа подпрограмм вывода на индикацию символов цифр.
;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
; Подготовительные операции.
;------------------------------------
CIFRA movwf Mem ; Сохранение, в регистре Mem, содержимого текущего LEDа.
movlw .8 ; В регистр Count записывается
movwf Count ; число проходов.
;====================================================================================================
; Циклическая ПП вывода на индикацию символов цифр.
;====================================================================================================
; В любой из таблиц вычисляемых переходов, начинаем выводить на индикацию символ, начиная от крайнего
; левого столбца и заканчивая крайним правым (в данном случае, знакоместо состоит из 8-ми столбцов).
;----------------------------------------------------------------------------------------------------
SNOVA movf Count,W ; Count -> W.
sublw .8 ; .8 - Count = ... (результат -> в W).
movwf Reg ; W -> Reg ("прыжковое" число копируется в Reg).
;-------------------------------------
; Для символа "0".
;-------------------------------------
movf Mem,W ; Mem -> W
sublw 0 ; 0 - W = ... (это символ “0” или другой ?)
btfss STATUS,Z ; Результат =0 или не=0 (нужно выводить на
; индикацию символ "0" или не нужно)?
goto $+3 ; Если не=0, то ПП "знакогенератора" символа "0"
; не вызывается (переход в следующую проверку).
call SIMVOL_0 ; Если =0, то вызывается ПП "знакогенератора" символа "0"
;-> Возврат по стеку из ПП SIMVOL_0 ; (после возврата, код текущего столбца “лежит” в W).
goto OBHOD ; Обход остальных проверок (переход в концовку процедуры).
;-------------------------------------
; Для символа "1".
;-------------------------------------
movf Mem,W ;
sublw 1 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_1 ; Переход в "знакогенератор" символа "1".
goto OBHOD ;
;-------------------------------------
; Для символа "2".
;-------------------------------------
movf Mem,W ;
sublw 2 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_2 ; Переход в "знакогенератор" символа "2".
goto OBHOD ;
;-------------------------------------
; Для символа "3".
;-------------------------------------
movf Mem,W ;
sublw 3 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_3 ; Переход в "знакогенератор" символа "3".
goto OBHOD ;
;-------------------------------------
; Для символа "4".
;-------------------------------------
movf Mem,W ;
sublw 4 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_4 ; Переход в "знакогенератор" символа "4".
goto OBHOD ;
;-------------------------------------
; Для символа "5".
;-------------------------------------
movf Mem,W ;
sublw 5 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_5 ; Переход в "знакогенератор" символа "5".
goto OBHOD ;
;-------------------------------------
; Для символа "6".
;-------------------------------------
movf Mem,W ;
sublw 6 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_6 ; Переход в "знакогенератор" символа "6".

53
goto OBHOD ;
;-------------------------------------
; Для символа "7".
;-------------------------------------
movf Mem,W ;
sublw 7 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_7 ; Переход в "знакогенератор" символа "7".
goto OBHOD ;
;-------------------------------------
; Для символа "8".
;-------------------------------------
movf Mem,W ;
sublw 8 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_8 ; Переход в "знакогенератор" символа "8".
goto OBHOD ;
;-------------------------------------
; Для символа "9".
;-------------------------------------
movf Mem,W ;
sublw 9 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_9 ; Переход в "знакогенератор" символа "9".
goto OBHOD ;
;-----------------------------------------------------
; Для символа "пробел" (под гашение незначащих нолей).
;-----------------------------------------------------
movf Mem,W ;
sublw .10 ; Аналогично.
btfss STATUS,Z ;
return ; Если проверяемое число более .10 (символ не подлежит
; отображению), то вывод на индикацию программно блокируется
; (путём возврата из ПП CIFRA).
call SIMVOL_NO ; Переход в "знакогенератор" символа "пробел".
;-------------------------------------
; Концовка.
;-------------------------------------
OBHOD movwf PORTB ; Код текущего столбца копируется в регистр PORTB.
nop ; Символизирует управление графическим модулем: обеспечение
; условия работы с байтами данных, стробирование, (т.е. вывод на
; индикацию), обеспечение условия работы с командами.
decfsz Count,F ; Count - 1 = ...
goto SNOVA ; Если результат не=0, то переход на начало вывода
; на индикацию следующего столбца.
return ; Если результат =0 (т.е. текущий символ выведен на индикацию),
; то возврат по стеку в то "место", из которого произошел вызов
; ПП CIFRA (переход на работу со следующим символом, а если
; текущий символ является последним, то выход из процедуры).
;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
; ................................ ; Символизирует
; ................................ : часть программы.
;***************************************************************************************************
end ; Конец программы.

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


перехода вызываемой ПП SIMVOL_1, а через другие таблицы не “проходит”
(см. соответствующие комментарии к тексту программы) , но этого вполне достаточно для того чтобы
убедиться в “безглюковой” работе (если эта “граница пройдёт” через любую из других таблиц вычисляемых
переходов, то работа будет такой же “безглюковой”).

Программа, в состав которой входит группа универсальных ПП вычисляемого


перехода, с идентификаторами конца таблиц, в приложении к произвольному
порядку формирования “прыжковых” чисел, выглядит так:

Программа №12 (для графического модуля на основе м/к KS0108)


;***************************************************************************************************
; Программа, позволяющая проверить работу вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
54
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; “Прыжковый” регистр.
Mem ; Регистр хранения содержимого текущего LEDx
;--------------------------
; Вспомогательные регистры:
;--------------------------
LED7 ; ----“----
LED6 ; ----“----
LED5 ; ----“----
LED4 ; Регистры результата
LED3 ; 2_10 преобразования.
LED2 ; ----“----
LED1 ; ----“----
LED0 ; ----“----
Flag ; Регистр флагов (для контроля событий
; вывода символов на индикацию).
;--------------------------
endc ; Конец блока.
;-----------------------------------------------------------------------------------------------
; “Межблочная граница” проходит через таблицу вычисляемого перехода (см. окно Program Memory),
; но при желании, её можно адресно сместить. При проверке, должна работать только одна из
; “нижележащих” директив org ... , а остальные должны быть заблокированы.
;-----------------------------------------------------------------------------------------------
; Через одну из таблиц вычисляемого перехода проходит
; “межблочная граница” 0-го и 1-го блоков.
;----------------------------------------------------
org 0E2h ; Текст программы адресно смещён так, что верхняя часть ПП
; “знакогенератора” символа “1” (SIMVOL_1) “лежит” в блоке 0,
; а нижняя её часть, в блоке 1.
; “Глюк” будет при переходе от 0x84 к 0xFE.
;----------------------------------------------------
; Через одну из таблиц вычисляемого перехода проходит
; “межблочная граница” 1-го и 2-го блоков.
;----------------------------------------------------
;;; org 1E2h ; Текст программы адресно смещён так, что верхняя часть ПП
;;; ; “знакогенератора” символа “1” (SIMVOL_1) “лежит” в блоке 1,
;;; ; а нижняя её часть, в блоке 2.
;;; ; “Глюк” будет при переходе от 0x84 к 0xFE.
;----------------------------------------------------
; Через одну из таблиц вычисляемого перехода проходит
; “межблочная граница” 2-го и 3-го блоков.
;----------------------------------------------------
;;; org 2E2h ; Текст программы PC-смещён так, что верхняя часть ПП
;;; ; “знакогенератора” символа “1” (SIMVOL_1) “лежит” в блоке 2,
;;; ; а нижняя её часть, в блоке 3.
;;; ; “Глюк” будет при переходе от 0x84 к 0xFE.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

; ................................ ; Часть программы.


;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Вызываемые ПП преобразования двоичного кода в код столбцов символов, выводимых на индикацию в
; дисплей графического модуля (программно организованный “знакогенератор”).
; Линии данных графического модуля подключены к выводам порта В.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Для символа "0".
;------------------------------------
SIMVOL_0 movlw high PC_ADR0; Установка (через W), в PCH (посредством PCLATH), числового
movwf PCLATH ; значения старшего байта адреса той команды, которая помечена
; меткой PC_ADR0.
;---------------------------------------------------
; Будем “прыгать” в текущий PC-блок или в следующий?
;---------------------------------------------------
movf Reg,W ; “Прыжковое” число копируется в W.
addlw low PC_ADR0 ; Суммирование "прыжкового" числа и числового значения младшего
; байта адреса той команды, которая помечена меткой PC_ADR0.
btfsc STATUS,C ; Перенос был или нет?
incf PCLATH,F ; Если перенос был (соответствует работе в следующем PC-блоке),
; то PCLATH+1=... (то есть, PCH+1=...).
; Если переноса не было (соответствует работе в текущем
; PC-блоке), то команда инкремента не исполняется.
movf Reg,W ; “Прыжковое” число копируется в W.

addwf PCL,F ; PCL + W = ...


PC_ADR0 dt 0x00,0x00,0x7C,0xA2,0x92,0x8A,0x7C,0x00,0xFF ; 8-столбцовый код символа “0”.
;------------------------------------
; Для символа "1".
;------------------------------------
SIMVOL_1 movlw high PC_ADR1;

55
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR1 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR1 dt 0x00,0x00,0x00,0x84,0xFE,0x80,0x00,0x00,0xFF ; 8-столбцовый код символа “1”.
;------------------------------------
; Для символа "2".
;------------------------------------
SIMVOL_2 movlw high PC_ADR2;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR2 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR2 dt 0x00,0x00,0x84,0xC2,0xA2,0x92,0x8C,0x00,0xFF ; 8-столбцовый код символа “2”.
;------------------------------------
; Для символа "3".
;------------------------------------
SIMVOL_3 movlw high PC_ADR3;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR3 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR3 dt 0x00,0x00,0x42,0x82,0x8A,0x96,0x62,0x00,0xFF ; 8-столбцовый код символа “3”.
;------------------------------------
; Для символа "4".
;------------------------------------
SIMVOL_4 movlw high PC_ADR4;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR4 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR4 dt 0x00,0x00,0x30,0x28,0x24,0xFE,0x20,0x00,0xFF ; 8-столбцовый код символа “4”.
;------------------------------------
; Для символа "5".
;------------------------------------
SIMVOL_5 movlw high PC_ADR5;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR5 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR5 dt 0x00,0x00,0x4E,0x8A,0x8A,0x8A,0x72,0x00,0xFF ; 8-столбцовый код символа “5”.
;------------------------------------
; Для символа "6".
;------------------------------------
SIMVOL_6 movlw high PC_ADR6;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR6 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR6 dt 0x00,0x00,0x78,0x94,0x92,0x92,0x60,0x00,0xFF ; 8-столбцовый код символа “6”.
;------------------------------------
; Для символа "7".
;------------------------------------
SIMVOL_7 movlw high PC_ADR7;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR7 ; только работа
btfsc STATUS,C ; с другой

56
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR7 dt 0x00,0x00,0x06,0x02,0xE2,0x12,0x0E,0x00,0xFF ; 8-столбцовый код символа “7”.
;------------------------------------
; Для символа "8".
;------------------------------------
SIMVOL_8 movlw high PC_ADR8;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR8 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR8 dt 0x00,0x00,0x6C,0x92,0x92,0x92,0x6C,0x00,0xFF ; 8-столбцовый код символа “8”.
;------------------------------------
; Для символа "9".
;------------------------------------
SIMVOL_9 movlw high PC_ADR9;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADR9 ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADR9 dt 0x00,0x00,0x0C,0x92,0x92,0x52,0x3C,0x00,0xFF ; 8-столбцовый код символа “9”.
;-----------------------------------------------------
; Для символа "пробел" (под гашение незначащих нолей).
;-----------------------------------------------------
SIMVOL_NO movlw high PC_ADRNO;
movwf PCLATH ;
movf Reg,W ; Аналогично,
addlw low PC_ADRNO ; только работа
btfsc STATUS,C ; с другой
incf PCLATH,F ; таблицей.
movf Reg,W ;

addwf PCL,F ; PCL + W = ...


PC_ADRNO dt 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF ; 8-столбцовый код символа
; “пробел”.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; ................................ ; Символизирует
; ................................ : часть программы.
;********************************************************************************************
; Начало программы.
;********************************************************************************************
START bsf STATUS,RP0 ; Переход в 1-й банк. <-- ТОЧКА ОСТАНОВКИ
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Переход в 0-й банк.
;----------------------------------------------------------------------------------------------------
; Примечание: прежде чем выводить данные на индикацию, нужно, тем или иным способом их заиметь.
; Обычно, результат “оседает” в N-байтном регистре (в двоичном виде. В детали не вдаюсь).
; Далее, содержимое N-байтного регистра нужно “протащить” через соответствующую ПП 2_10
; преобразования (варианты “расписаны” в статье “ПП 2_10 преобразований”), но в данном случае,
; во избежание усложнения текста программы, результаты работы ПП BIN2_10 сымитированы (кстати,
; слово “сымитирован” пишется не через “и”, а через “ы”, а слово “имитация” пишется через “и”),
; например, так (можно и по-другому):
;----------------------------------------------------------------------------------------------------
; Имитация результатов работы ПП BIN2_10.
;----------------------------------------
movlw 1 ; В регистр LED7
movwf LED7 ; записано число 1, что соответствует символу “1”.
movlw 2 ; В регистр LED6
movwf LED6 ; записано число 2, что соответствует символу “2”.
movlw 3 ; В регистр LED5
movwf LED5 ; записано число 3, что соответствует символу “3”.
movlw 4 ; В регистр LED4
movwf LED4 ; записано число 4, что соответствует символу “4”.
movlw 5 ; В регистр LED3
movwf LED3 ; записано число 5, что соответствует символу “5”.
movlw 6 ; В регистр LED2
movwf LED2 ; записано число 6, что соответствует символу “6”.
movlw 7 ; В регистр LED1
movwf LED1 ; записано число 7, что соответствует символу “7”.
movlw 8 ; В регистр LED0
movwf LED0 ; записано число 8, что соответствует символу “8”.
;----------------------------------------------------------------------------------------------------

57
; В данном случае, результаты 2_10 преобразования “оседают” в регистрах LED7..LED0, что соответствует
; дальнейшему выводу на индикацию показания 12345678 (незначащих нолей нет).
;----------------------------------------------------------------------------------------------------
; Если это нужно, то в дальнейшем можно “протащить” результаты 2_10 преобразования через программную
; процедуру гашения незначащих нолей (можно с ней или без неё. Это зависит от желания конструктора).
; Принцип гашения прост: символ незначащего “0” программно заменяется на символ “пробел” (или
; “пусто”. Обычно, крайний правый ноль не гасится, но можно и загасить или сделать так, как хочется).
; Если, взамен любого из “вышележащих” чисел, “настукать” число .10, то можно отследить программный
; “механизм” вывода на индикацию символа “пробел”.
;----------------------------------------------------------------------------------------------------
clrf Flag ; Регистр флагов очищен (подготовка к “бит-регистрированию”
; событий вывода символов на индикацию).
;====================================================================================================
; Процедура вывода на индикацию, в дисплей графического модуля, символов, “привязанных” к числовым
; результатам предыдущей работы.
;====================================================================================================
; Подготовительные операции.
;------------------------------------
nop ; Символизирует управление графическим модулем: выбор кристалла,
; выбор страницы этого кристалла, выбор нужного столбца страницы,
; вывод инструкции “Set Address”, в данном случае, в порт В,
; строб под эту инструкцию.
;------------------------------------
; "Администраторская" группа команд.
;------------------------------------
movf LED7,W ; LED7 -> W.
call CIFRA ; Вызов ПП CIFRA.
;---> Возврат по стеку из ПП CIFRA
bsf Flag,7 ; Контроль окончания вывода на индикацию символа в 8-е,
; самое левое знакоместо (необязательная команда).
;------------------------------------
movf LED6,W ;
call CIFRA ; Аналогично, но для LED6.
bsf Flag,6 ; Контроль окончания вывода на индикацию символа в 7-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED5,W ;
call CIFRA ; Аналогично, но для LED5.
bsf Flag,5 ; Контроль окончания вывода на индикацию символа в 6-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED4,W ;
call CIFRA ; Аналогично, но для LED4.
bsf Flag,4 ; Контроль окончания вывода на индикацию символа в 5-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED3,W ;
call CIFRA ; Аналогично, но для LED3.
bsf Flag,3 ; Контроль окончания вывода на индикацию символа в 4-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED2,W ;
call CIFRA ; Аналогично, но для LED2.
bsf Flag,2 ; Контроль окончания вывода на индикацию символа в 3-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED1,W ;
call CIFRA ; Аналогично, но для LED1.
bsf Flag,1 ; Контроль окончания вывода на индикацию символа во 2-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED0,W ;
call CIFRA ; Аналогично, но для LED0.
bsf Flag,0 ; Контроль окончания вывода на индикацию символа в 1-е,
; самое правое знакоместо (необязательная команда).
;------------------------------------------------
; Окончание процедуры вывода данных на индикацию.
;------------------------------------------------
goto $ ; В данном, учебно-тренировочном случае, “мертвяк”,
; а в реале, переход туда, куда нужно
; (с целью продолжения исполнения программы).
;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
; Группа подпрограмм вывода на индикацию символов цифр.
;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
; Подготовительные операции.
;-------------------------------------
CIFRA movwf Mem ; Сохранение, в регистре Mem, содержимого текущего LEDа.
clrf Reg ; Начинаем с верхней строки таблицы вычисляемого перехода.
;====================================================================================================
; Циклическая ПП вывода на индикацию символов цифр.
;====================================================================================================
; Для символа "0".

58
;-------------------------------------
SNOVA movf Mem,W ; Mem -> W
sublw 0 ; 0 - W = ... (это символ “0” или другой ?)
btfss STATUS,Z ; Результат =0 или не=0 (нужно выводить на
; индикацию символ "0" или не нужно)?
goto $+3 ; Если не=0, то ПП "знакогенератора" символа "0"
; не вызывается (переход в следующую проверку).
call SIMVOL_0 ; Если =0, то вызывается ПП "знакогенератора" символа "0"
;-> Возврат по стеку из ПП SIMVOL_0 ; (после возврата, код текущего столбца “лежит” в W).
goto OBHOD ; Обход остальных проверок (переход в концовку процедуры).
;-------------------------------------
; Для символа "1".
;-------------------------------------
movf Mem,W ;
sublw 1 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_1 ; Переход в "знакогенератор" символа "1".
goto OBHOD ;
;-------------------------------------
; Для символа "2".
;-------------------------------------
movf Mem,W ;
sublw 2 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_2 ; Переход в "знакогенератор" символа "2".
goto OBHOD ;
;-------------------------------------
; Для символа "3".
;-------------------------------------
movf Mem,W ;
sublw 3 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_3 ; Переход в "знакогенератор" символа "3".
goto OBHOD ;
;-------------------------------------
; Для символа "4".
;-------------------------------------
movf Mem,W ;
sublw 4 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_4 ; Переход в "знакогенератор" символа "4".
goto OBHOD ;
;-------------------------------------
; Для символа "5".
;-------------------------------------
movf Mem,W ;
sublw 5 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_5 ; Переход в "знакогенератор" символа "5".
goto OBHOD ;
;-------------------------------------
; Для символа "6".
;-------------------------------------
movf Mem,W ;
sublw 6 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_6 ; Переход в "знакогенератор" символа "6".
goto OBHOD ;
;-------------------------------------
; Для символа "7".
;-------------------------------------
movf Mem,W ;
sublw 7 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_7 ; Переход в "знакогенератор" символа "7".
goto OBHOD ;
;-------------------------------------
; Для символа "8".
;-------------------------------------
movf Mem,W ;
sublw 8 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_8 ; Переход в "знакогенератор" символа "8".
goto OBHOD ;

59
;-------------------------------------
; Для символа "9".
;-------------------------------------
movf Mem,W ;
sublw 9 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_9 ; Переход в "знакогенератор" символа "9".
goto OBHOD ;
;-----------------------------------------------------
; Для символа "пробел" (под гашение незначащих нолей).
;-----------------------------------------------------
movf Mem,W ;
sublw .10 ; Аналогично.
btfss STATUS,Z ;
return ; Если проверяемое число более .10 (символ не подлежит
; отображению), то вывод на индикацию программно блокируется
; (путём возврата из ПП CIFRA).
call SIMVOL_NO ; Переход в "знакогенератор" символа "пробел".
;-------------------------------------
; Концовка.
;-------------------------------------
OBHOD movwf PORTB ; Код текущего столбца копируется в регистр PORTB.
sublw .255 ; .255 – W = ...
btfsc STATUS,Z ; Это .255 или другое число ?
return ; Если результат =0 (т.е. текущий символ выведен на индикацию),
; то переход на работу со следующим символом.
; Если не=0, то далее (продолжение вывода символа на индикацию).
nop ; Символизирует управление графическим модулем: обеспечение
; условия работы с байтами данных, стробирование, (т.е. вывод на
; индикацию), обеспечение условия работы с командами.
incf Reg,F ; Reg + 1 = ... Результат -> Reg (подготовка к “прыжку”
; в следующую строку таблицы вычисляемого перехода).
goto SNOVA ; Переход на начало вывода на индикацию следующего столбца.
;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
; ................................ ; Символизирует
; ................................ : часть программы.
;***************************************************************************************************
end ; Конец программы.

Число проходов не назначается.


Текущая таблица вычисляемого перехода будет последовательно отрабатываться до
тех пор, пока рабочая точка программы не “споткнётся” о признак конца таблицы (0xFF),
что означает окончание вывода на индикацию соответствующего символа.
После этого, по такому же принципу, на индикацию будут выведены и остальные
символы.
После этого à выход из процедуры (в данном случае, в виде “мертвяка”).

Принципы командноскоростной оптимизации процедур вычисляемых переходов

Результатом осуществления командноскоростной оптимизации является “дочерний”


выигрыш (по сравнению с “матерью”) по количеству команд и по скорости.
Более подробно об этом сказано в статье 2/72 “Обмена…”.
Оптимизации подлежат (если конструктор этого завожделеет и ему не лень этим заняться. Кстати, это признак
профессионализма) те “материнские” программные процедуры, содержимое которых
избыточно (в приложении к конечному результату работы, “этого добра навалом”. “Куда не ткнись”. Даже диву
даёшься).
Так как, во многих случаях, универсальность достигается в обмен на избыточность, то,
прежде всего, оптимизации подлежат универсальные, программные процедуры.
Таким образом, в приложении к предмету данного разговора, командноскоростной
оптимизации подлежат те программные процедуры, в которых используются операторы
high и low.
И в самом деле, если “межблочная граница” не проходит через таблицу (таблицы)
вычисляемого перехода, то какой смысл “городить избыточный огород (условно)”?
По здравому разумению, его нужно “городить” только тогда, когда через таблицу
(таблицы) вычисляемого перехода проходит “межблочная граница (границы)”.
Командноскоростную оптимизацию выгодно осуществить на конечной стадии работы,
либо в тех случаях, когда, по ходу “ваяния” программы, PC “забита под завязку” и

60
нужно освободить “PC-местечко” под реализацию тех задумок, которые запланированы,
но ещё не реализованы.
Методика осуществления этой разновидности командноскоростной оптимизации
относительно проста:

1. От начала работы над текстом программы и вплоть до её окончания (либо до


текущего заполнения PC типа “под завязку”), в целях обеспечения гарантированной
“безбяковости”, используются универсальные, избыточные процедуры.

2. То есть, “базис” текста программы сформирован и далее ничто не мешает


“навести вычисляемопереходный марафет”:
 Всё, что относится к работе с операторами high и low, блокируется (точками с
запятыми). При этом, нужно следить за тем, чтобы условия нормального
функционирования процедуры вычисляемого перехода были сохранены.
 Работа в MPLAB: ассемблирование.
 Открытие окна Program Memory, анализ его содержимого и ответ на вопрос:
“Межблочная граница проходит через таблицу заоптимизированного
вычисляемого перехода или не проходит”? (или во множественном числе, если таких таблиц
несколько).
 Если не проходит, то ранее заблокированное радостно удаляется (оптимизация
прошла успешно à минус N-команд и выигрыш по скорости).
 Если проходит, то нужно либо снять блокировки (отказ от оптимизации. “Возврат в
прежнее состояние”), либо, не снимая блокировок, сместить (“перебазировать”), в PC,
таблицу вычисляемого перехода таким образом, чтобы “межблочная граница”
не проходила через “заоптимизированную” таблицу (или во множественном числе, если
таких таблиц несколько). Если компромисс найден, то ранее заблокированное
удаляется, а если не найден, то можно “сваять” выгодный “гибрид” или
отказаться от оптимизации (“попытка - не пытка”).
 По окончании этой работы, производится ассемблирование и проверка
“родившегося в железе”.

Так как “лучше 1 раз увидеть, чем 100 раз услышать”, приведу 3 примера
командноскоростной оптимизации универсальных ПП вычисляемого перехода, с
идентификаторами (FFh) конца таблиц.

Программа №13 à оптимизация программы №6 (для ЖК-модуля на основе м/к HD44780)


;***************************************************************************************************
; Программа, позволяющая проверить работу ПП универсального вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; “Прыжковый” регистр.
endc ; Конец блока.
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; ВНИМАНИЕ! ПРИ ПРОВЕРКЕ, ДОЛЖНА РАБОТАТЬ ТОЛЬКО ОДНА ИЗ “НИЖЕЛЕЖАЩИХ” ДИРЕКТИВ org ...
; ОСТАЛЬНЫЕ ДОЛЖНЫ БЫТЬ ЗАБЛОКИРОВАНЫ + ВЫПОЛНЕНИЕ “НИЖЕЛЕЖАЩЕЙ” ...!!!! ИНСТРУКЦИИ !!!...
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; Варианты “невыхода” за “границы” блока (командноскоростная оптимизация возможна).
;-----------------------------------------------------------------------------------------------
org 0 ; Первая команда программы “дислоцируется” в блоке 0.
;;; org 100h ; Первая команда программы “дислоцируется” в блоке 1.
;;; org 200h ; Первая команда программы “дислоцируется” в блоке 2.
;;; org 300h ; Первая команда программы “дислоцируется” в блоке 3.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
61
; Вызываемая ПП преобразования двоичного кода в код символа, выводимого на индикацию в дисплей
; ЖК-модуля. Линии данных ЖК-модуля подключены к выводам порта В.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; В таблице “лежит” адресный код символов, формирующих надпись ЗАЩИТА ВЫКЛЮЧЕНА
;------------------------------------------------------------------------------
TABLE
;;; movlw high PC_ADR ; Установка (через W), в PCH (посредством PCLATH), числового
;;; movwf PCLATH ; значения старшего байта адреса той команды, которая помечена
; меткой PC_ADR.
;---------------------------------------------------
; Будем “прыгать” в текущий PC-блок или в следующий?
;---------------------------------------------------
;;; movf Reg,W ; “Прыжковое” число копируется в W.
;;; addlw low PC_ADR ; Суммирование "прыжкового" числа и числового значения
; младшего байта адреса той команды, которая помечена
; меткой PC_ADR.
;;; btfsc STATUS,C ; Перенос был или нет?
;;; incf PCLATH,F ; Если перенос был (соответствует работе в следующем
; PC-блоке), то PCLATH+1=... (то есть, PCH+1=...).
; Если переноса не было (соответствует работе в текущем
; PC-блоке), то команда инкремента не исполняется.
movf Reg,W ; “Прыжковое” число копируется в W.
;------------------------------------
; Таблица вычисляемого перехода.
;------------------------------------
addwf PCL,F ; PCL + W = ...
;;;PC_ADR
dt 0xA4,0x41,0xE2,0xA5,0x54,0x41,0x20,0x42 ; ЗАЩИТА
dt 0xAE,0x4B,0xA7,0xB0,0xAB,0x45,0x48,0x41,0xFF ; ВЫКЛЮЧЕНА
;-----------------------------------------------------------------------------------------------
; ................................ ; Символизирует
; ................................ : часть программы.
;********************************************************************************************
; Начало программы.
;********************************************************************************************
START bsf STATUS,RP0 ; Переход в 1-й банк. <-- ТОЧКА ОСТАНОВКИ
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Переход в 0-й банк.
;===================================================================================================
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИНСТРУКЦИЯ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;===================================================================================================
; Если разблокировано org 0, то заблокировать всё то, что ниже выделено красным цветом.
;---------------------------------------------------------------------------------------------------
; Разблокировать, если разблокировано org 100h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 1 ; Так как 1-я команда ПП TABLE_1 “лежит”
;;; movwf PCLATH ; в блоке 1, то выбор блока 1.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 200h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 2 ; Так как 1-я команда ПП TABLE_1 “лежит”
;;; movwf PCLATH ; в блоке 2, то выбор блока 2.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 300h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 3 ; Так как 1-я команда ПП TABLE_1 “лежит”
;;; movwf PCLATH ; в блоке 3, то выбор блока 3.
;==================================================================================================
; Процедура вывода на индикацию надписи ЗАЩИТА ВЫКЛЮЧЕНА.
;==================================================================================================
nop ; Символизирует управление ЖК-модулем: установка курсора
; в начало выбранной строки дисплея (в крайнее левое знакоместо)
; и последующее стробирование.
clrf Reg ; Начинаем от ноля (Reg=0).
SNOVA call TABLE ; Вызов ПП TABLE.
; --> Возврат по стеку из ПП TABLE (“добытое лежит” в W).
movwf PORTB ; Адресный выбор ячейки ЖК-знакогенератора (подготовка
; к выводу текущего символа на индикацию).
sublw .255 ; .255 – W = ...
btfsc STATUS,Z ; Это .255 или другое число ?
goto VIHOD ; Если .255, то окончание вывода надписи на индикацию
; и выход из процедуры.
; Если другое число, то далее
; (продолжение вывода надписи на индикацию).
nop ; Символизирует управление ЖК-модулем: установка режима
; записи данных и последующее стробирование (т.е. фактический
; вывод символа на индикацию).
incf Reg,F ; Reg + 1 = ... Результат -> Reg.
goto SNOVA ; Продолжаем перебор далее.

62
;------------------------------------
VIHOD goto $ ; Если все, то “мертвяк” (если нужна многоцикличность,
; то, например, goto START).
;***************************************************************************************************
end ; Конец программы.

Это относительно простой вариант командноскоростной оптимизации (одна таблица малого


“объёма”), применимый к случаю “невыхода” таблицы вычисляемого перехода за “границы”
PC-блока.
Серым цветом выделено то, что подлежит удалению.
А это вариант посложнее (2 таблицы большего “объёма”):

Программа №14 à оптимизация программы №9 (для графического модуля на основе м/к KS0108)
;***************************************************************************************************
; Программа, позволяющая проверить работу ПП универсального вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; “Прыжковый” регистр.
endc ; Конец блока.
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; ВНИМАНИЕ! ПРИ ПРОВЕРКЕ, ДОЛЖНА РАБОТАТЬ ТОЛЬКО ОДНА ИЗ “НИЖЕЛЕЖАЩИХ” ДИРЕКТИВ org ...
; ОСТАЛЬНЫЕ ДОЛЖНЫ БЫТЬ ЗАБЛОКИРОВАНЫ + ВЫПОЛНЕНИЕ “НИЖЕЛЕЖАЩЕЙ” ...!!!! ИНСТРУКЦИИ !!!...
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; Варианты “невыхода” за “границы” блока (командноскоростная оптимизация возможна).
;-----------------------------------------------------------------------------------------------
org 0 ; Первая команда программы “дислоцируется” в блоке 0.
;;; org 100h ; Первая команда программы “дислоцируется” в блоке 1.
;;; org 200h ; Первая команда программы “дислоцируется” в блоке 2.
;;; org 300h ; Первая команда программы “дислоцируется” в блоке 3.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Вызываемые ПП преобразования двоичного кода в код столбцов символов, выводимых на индикацию в
; дисплей графического модуля. Линии данных графического модуля подключены к выводам порта В.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; На индикацию, в строку дисплея, в 2 приёма (так как строка дисплея состоит из двух “однономерных”
; строк кристаллов), выводится надпись ЗАЩИТА ВЫКЛЮЧЕНА .
;====================================================================================================
; Для надписи ЗАЩИТА В .
;====================================================================================================
TABLE_1
;;; movlw high PC_ADR1; Установка (через W), в PCH (посредством PCLATH), числового
;;; movwf PCLATH ; значения старшего байта адреса той команды, которая помечена
; меткой PC_ADR1.
;---------------------------------------------------
; Будем “прыгать” в текущий PC-блок или в следующий?
;---------------------------------------------------
;;; movf Reg,W ; “Прыжковое” число копируется в W.
;;; addlw low PC_ADR1 ; Суммирование "прыжкового" числа и числового значения
; младшего байта адреса той команды, которая помечена
; меткой PC_ADR1.
;;; btfsc STATUS,C ; Перенос был или нет?
;;; incf PCLATH,F ; Если перенос был (соответствует работе в следующем
; PC-блоке), то PCLATH+1=... (то есть, PCH+1=...).
; Если переноса не было (соответствует работе в текущем
; PC-блоке), то команда инкремента не исполняется.
movf Reg,W ; “Прыжковое” число копируется в W.
;----------------------------------------------------
; Таблица вычисляемого перехода под надпись ЗАЩИТА В.
;----------------------------------------------------
addwf PCL,F ; PCL + W = ...
;;;PC_ADR1
dt 0x00,0x00,0x44,0x82,0x92,0x92,0x6C,0x00 ; символ З
dt 0x00,0x00,0xFC,0x22,0x22,0x22,0xFC,0x00 ; символ А
dt 0x00,0x00,0x7E,0x40,0x7E,0x40,0x7E,0xC0 ; символ Щ
dt 0x00,0x00,0xFE,0x20,0x10,0x08,0xFE,0x00 ; символ И
dt 0x00,0x00,0x02,0x02,0xFE,0x02,0x02,0x00 ; символ Т
dt 0x00,0x00,0xFC,0x22,0x22,0x22,0xFC,0x00 ; символ А

63
dt 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; символ пусто (пробел)
dt 0x00,0x00,0xFE,0x92,0x92,0x92,0x6C,0x00,0xFF ; символ В и конец таблицы
;====================================================================================================
; Для надписи ЫКЛЮЧЕНА .
;====================================================================================================
TABLE_2
;;; movlw high PC_ADR2;
;;; movwf PCLATH ;
;;; movf Reg,W ; Аналогично,
;;; addlw low PC_ADR2 ; только работа
;;; btfsc STATUS,C ; с другой
;;; incf PCLATH,F ; таблицей.
movf Reg,W ;
;----------------------------------------------------
; Таблица вычисляемого перехода под надпись ЫКЛЮЧЕНА.
;----------------------------------------------------
addwf PCL,F ; PCL + W = ...
;;;PC_ADR2
dt 0x00,0xFE,0x88,0x88,0x70,0x00,0xFE,0x00 ; символ Ы
dt 0x00,0x00,0xFE,0x10,0x28,0x44,0x82,0x00 ; символ К
dt 0x00,0x40,0x82,0x7E,0x02,0x02,0xFE,0x00 ; символ Л
dt 0x00,0xFE,0x10,0x7C,0x82,0x82,0x7C,0x00 ; символ Ю
dt 0x00,0x00,0x0E,0x10,0x10,0x10,0xFE,0x00 ; символ Ч
dt 0x00,0x00,0xFE,0x92,0x92,0x92,0x82,0x00 ; символ Е
dt 0x00,0x00,0xFE,0x10,0x10,0x10,0xFE,0x00 ; символ Н
dt 0x00,0x00,0xFC,0x22,0x22,0x22,0xFC,0x00,0xFF ; символ А и конец таблицы
;-----------------------------------------------------------------------------------------------
; ................................ ; Символизирует
; ................................ : часть программы.
;********************************************************************************************
; Начало программы.
;********************************************************************************************
START bsf STATUS,RP0 ; Переход в 1-й банк. <-- ТОЧКА ОСТАНОВКИ
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Переход в 0-й банк.
;===================================================================================================
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИНСТРУКЦИЯ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;===================================================================================================
; Если разблокировано org 0, то заблокировать всё то, что ниже выделено красным цветом.
;---------------------------------------------------------------------------------------------------
; Разблокировать, если разблокировано org 100h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 1 ; Так как 1-я команда ПП TABLE_1 “лежит”
;;; movwf PCLATH ; в блоке 1, то выбор блока 1.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 200h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 2 ; Так как 1-я команда ПП TABLE_1 “лежит”
;;; movwf PCLATH ; в блоке 2, то выбор блока 2.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 300h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 3 ; Так как 1-я команда ПП TABLE_1 “лежит”
;;; movwf PCLATH ; в блоке 3, то выбор блока 3.
;==================================================================================================
; Процедура вывода на индикацию надписи ЗАЩИТА ВЫКЛЮЧЕНА.
; Например, выводим эту надпись в 1-ю дисплейную строку.
;==================================================================================================
; Вывод на индикацию, в 1-ю строку 1-го кристалла, надписи ЗАЩИТА В.
;-------------------------------------------------------------------
nop ; Символизирует управление графическим модулем: выбор 1-го
; (левого) кристалла, выбор 1-й страницы, выбор 1-го столбца.
clrf Reg ; Начинаем с верхней строки таблицы вычисляемого перехода.
SNOVA_1 call TABLE_1 ; Вызов ПП TABLE_1.
; --> Возврат по стеку из ПП TABLE (“добытое лежит” в W).
movwf PORTB ; Подготовка к выводу на индикацию текущего столбца
; (предустановка кода столбца на 8-ми линиях данных
; графического модуля).
sublw .255 ; .255 – W = ...
btfsc STATUS,Z ; Это .255 или другое число ?
goto VIHOD_1 ; Если .255, то окончание вывода на индикацию надписи ЗАЩИТА В
; и переход на вывод на индикацию надписи ЫКЛЮЧЕНА.
; Если другое число, то далее
; (продолжение вывода надписи на индикацию).
nop ; Символизирует управление графическим модулем: обеспечение
; условия работы с байтами данных, стробирование, (т.е. вывод на
; индикацию), обеспечение условия работы с командами.
incf Reg,F ; Reg + 1 = ... Результат -> Reg (подготовка к “прыжку”
; в следующую строку таблицы вычисляемого перехода).

64
goto SNOVA_1 ; Продолжаем перебор далее.
;-------------------------------------------------------------------
; Вывод на индикацию, в 1-ю строку 2-го кристалла, надписи ЫКЛЮЧЕНА.
;-------------------------------------------------------------------
VIHOD_1 nop ; Символизирует управление графическим модулем: выбор 2-го
; (правого) кристалла, выбор 1-й страницы, выбор 1-го столбца.
clrf Reg ;
SNOVA_2 call TABLE_2 ;
movwf PORTB ; Аналогично,
sublw .255 ; только с вызовом
btfsc STATUS,Z ; ПП TABLE_2.
goto VIHOD_2 ; Окончание вывода на индикацию надписи и и переход туда,
; куда нужно (выход из процедуры). Если нужна многоцикличность,
; то, например, goto START.
nop ;
incf Reg,F ;
goto SNOVA_2 ;
;------------------------------------
VIHOD_2 goto $ ; В данном, примитивном случае, “мертвяк”,
; но в реале – продолжение исполнения программы.
;***************************************************************************************************
end ; Конец программы.

При тех значениях org … , которые указаны в тексте программы, обе таблицы “лежат”
внутри заданного блока, поэтому оптимизации подлежат обе табличные процедуры, но
если задать такие значения org … , которые “нарушают эту идиллию”, то нужно
- либо разблокировать заблокированные команды одной из этих процедур,
- либо так PC-сместить табличные процедуры, чтобы это “нарушение идиллии” было
устранено.
Ещё более сложный случай (11 таблиц):

Программа №15 à оптимизация программы №12 (для графического модуля на основе м/к KS0108)
;***************************************************************************************************
; Программа, позволяющая проверить работу вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; “Прыжковый” регистр.
Mem ; Регистр хранения содержимого текущего LEDx
;--------------------------
; Вспомогательные регистры:
;--------------------------
LED7 ; ----“----
LED6 ; ----“----
LED5 ; ----“----
LED4 ; Регистры результата
LED3 ; 2_10 преобразования.
LED2 ; ----“----
LED1 ; ----“----
LED0 ; ----“----
Flag ; Регистр флагов (для контроля событий
; вывода символов на индикацию).
;--------------------------
endc ; Конец блока.
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; ВНИМАНИЕ! ПРИ ПРОВЕРКЕ, ДОЛЖНА РАБОТАТЬ ТОЛЬКО ОДНА ИЗ “НИЖЕЛЕЖАЩИХ” ДИРЕКТИВ org ...
; ОСТАЛЬНЫЕ ДОЛЖНЫ БЫТЬ ЗАБЛОКИРОВАНЫ + ВЫПОЛНЕНИЕ “НИЖЕЛЕЖАЩЕЙ” ...!!!! ИНСТРУКЦИИ !!!...
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; Варианты “невыхода” за “границы” блока (командноскоростная оптимизация возможна).
;-----------------------------------------------------------------------------------------------
org 0 ; Первая команда программы “дислоцируется” в блоке 0.
;;; org 100h ; Первая команда программы “дислоцируется” в блоке 1.
;;; org 200h ; Первая команда программы “дислоцируется” в блоке 2.
;;; org 300h ; Первая команда программы “дислоцируется” в блоке 3.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Вызываемые ПП преобразования двоичного кода в код столбцов символов, выводимых на индикацию в

65
; дисплей графического модуля (программно организованный “знакогенератор”).
; Линии данных графического модуля подключены к выводам порта В.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Для символа "0".
;------------------------------------
SIMVOL_0
;;; movlw high PC_ADR0; Установка (через W), в PCH (посредством PCLATH), числового
;;; movwf PCLATH ; значения старшего байта адреса той команды, которая помечена
; меткой PC_ADR0.
;---------------------------------------------------
; Будем “прыгать” в текущий PC-блок или в следующий?
;---------------------------------------------------
;;; movf Reg,W ; “Прыжковое” число копируется в W.
;;; addlw low PC_ADR0 ; Суммирование "прыжкового" числа и числового значения
; младшего байта адреса той команды, которая помечена
; меткой PC_ADR0.
;;; btfsc STATUS,C ; Перенос был или нет?
;;; incf PCLATH,F ; Если перенос был (соответствует работе в следующем
; PC-блоке), то PCLATH+1=... (то есть, PCH+1=...).
; Если переноса не было (соответствует работе в текущем
; PC-блоке), то команда инкремента не исполняется.
movf Reg,W ; “Прыжковое” число копируется в W.

addwf PCL,F ; PCL + W = ...


;;;PC_ADR0
dt 0x00,0x00,0x7C,0xA2,0x92,0x8A,0x7C,0x00,0xFF ; 8-столбцовый код символа “0”.
;------------------------------------
; Для символа "1".
;------------------------------------
SIMVOL_1
;;; movlw high PC_ADR1;
;;; movwf PCLATH ;
;;; movf Reg,W ; Аналогично,
;;; addlw low PC_ADR1 ; только работа
;;; btfsc STATUS,C ; с другой
;;; incf PCLATH,F ; таблицей.
movf Reg,W ;
addwf PCL,F ; PCL + W = ...
;;;PC_ADR1
dt 0x00,0x00,0x00,0x84,0xFE,0x80,0x00,0x00,0xFF ; 8-столбцовый код символа “1”.
;------------------------------------
; Для символа "2".
;------------------------------------
SIMVOL_2
;;; movlw high PC_ADR2;
;;; movwf PCLATH ;
;;; movf Reg,W ; Аналогично,
;;; addlw low PC_ADR2 ; только работа
;;; btfsc STATUS,C ; с другой
;;; incf PCLATH,F ; таблицей.
movf Reg,W ;
addwf PCL,F ; PCL + W = ...
;;;PC_ADR2
dt 0x00,0x00,0x84,0xC2,0xA2,0x92,0x8C,0x00,0xFF ; 8-столбцовый код символа “2”.
;------------------------------------
; Для символа "3".
;------------------------------------
SIMVOL_3
;;; movlw high PC_ADR3;
;;; movwf PCLATH ;
;;; movf Reg,W ; Аналогично,
;;; addlw low PC_ADR3 ; только работа
;;; btfsc STATUS,C ; с другой
;;; incf PCLATH,F ; таблицей.
movf Reg,W ;
addwf PCL,F ; PCL + W = ...
;;;PC_ADR3
dt 0x00,0x00,0x42,0x82,0x8A,0x96,0x62,0x00,0xFF ; 8-столбцовый код символа “3”.
;------------------------------------
; Для символа "4".
;------------------------------------
SIMVOL_4
;;; movlw high PC_ADR4;
;;; movwf PCLATH ;
;;; movf Reg,W ; Аналогично,
;;; addlw low PC_ADR4 ; только работа
;;; btfsc STATUS,C ; с другой
;;; incf PCLATH,F ; таблицей.
movf Reg,W ;
addwf PCL,F ; PCL + W = ...
;;;PC_ADR4
dt 0x00,0x00,0x30,0x28,0x24,0xFE,0x20,0x00,0xFF ; 8-столбцовый код символа “4”.

66
;------------------------------------
; Для символа "5".
;------------------------------------
SIMVOL_5
;;; movlw high PC_ADR5;
;;; movwf PCLATH ;
;;; movf Reg,W ; Аналогично,
;;; addlw low PC_ADR5 ; только работа
;;; btfsc STATUS,C ; с другой
;;; incf PCLATH,F ; таблицей.
movf Reg,W ;
addwf PCL,F ; PCL + W = ...
;;;PC_ADR5
dt 0x00,0x00,0x4E,0x8A,0x8A,0x8A,0x72,0x00,0xFF ; 8-столбцовый код символа “5”.
;------------------------------------
; Для символа "6".
;------------------------------------
SIMVOL_6
;;; movlw high PC_ADR6;
;;; movwf PCLATH ;
;;; movf Reg,W ; Аналогично,
;;; addlw low PC_ADR6 ; только работа
;;; btfsc STATUS,C ; с другой
;;; incf PCLATH,F ; таблицей.
movf Reg,W ;
addwf PCL,F ; PCL + W = ...
;;;PC_ADR6
dt 0x00,0x00,0x78,0x94,0x92,0x92,0x60,0x00,0xFF ; 8-столбцовый код символа “6”.
;------------------------------------
; Для символа "7".
;------------------------------------
SIMVOL_7
;;; movlw high PC_ADR7;
;;; movwf PCLATH ;
;;; movf Reg,W ; Аналогично,
;;; addlw low PC_ADR7 ; только работа
;;; btfsc STATUS,C ; с другой
;;; incf PCLATH,F ; таблицей.
movf Reg,W ;
addwf PCL,F ; PCL + W = ...
;;;PC_ADR7
dt 0x00,0x00,0x06,0x02,0xE2,0x12,0x0E,0x00,0xFF ; 8-столбцовый код символа “7”.
;------------------------------------
; Для символа "8".
;------------------------------------
SIMVOL_8
;;; movlw high PC_ADR8;
;;; movwf PCLATH ;
;;; movf Reg,W ; Аналогично,
;;; addlw low PC_ADR8 ; только работа
;;; btfsc STATUS,C ; с другой
;;; incf PCLATH,F ; таблицей.
movf Reg,W ;
addwf PCL,F ; PCL + W = ...
;;;PC_ADR8
dt 0x00,0x00,0x6C,0x92,0x92,0x92,0x6C,0x00,0xFF ; 8-столбцовый код символа “8”.
;------------------------------------
; Для символа "9".
;------------------------------------
SIMVOL_9
;;; movlw high PC_ADR9;
;;; movwf PCLATH ;
;;; movf Reg,W ; Аналогично,
;;; addlw low PC_ADR9 ; только работа
;;; btfsc STATUS,C ; с другой
;;; incf PCLATH,F ; таблицей.
movf Reg,W ;
addwf PCL,F ; PCL + W = ...
;;;PC_ADR9
dt 0x00,0x00,0x0C,0x92,0x92,0x52,0x3C,0x00,0xFF ; 8-столбцовый код символа “9”.
;-----------------------------------------------------
; Для символа "пробел" (под гашение незначащих нолей).
;-----------------------------------------------------
SIMVOL_NO
;;; movlw high PC_ADRNO;
;;; movwf PCLATH ;
;;; movf Reg,W ; Аналогично,
;;; addlw low PC_ADRNO ; только работа
;;; btfsc STATUS,C ; с другой
;;; incf PCLATH,F ; таблицей.
movf Reg,W ;
addwf PCL,F ; PCL + W = ...

67
;;;PC_ADRNO
dt 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF ; 8-столбцовый код символа
; “пробел”.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; ................................ ; Символизирует
; ................................ : часть программы.
;********************************************************************************************
; Начало программы.
;********************************************************************************************
START bsf STATUS,RP0 ; Переход в 1-й банк. <-- ТОЧКА ОСТАНОВКИ
clrf TRISB ; Все выводы порта В работают “на выход”.
bcf STATUS,RP0 ; Переход в 0-й банк.
;===================================================================================================
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИНСТРУКЦИЯ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;===================================================================================================
; Если разблокировано org 0, то заблокировать всё то, что ниже выделено красным цветом.
;---------------------------------------------------------------------------------------------------
; Разблокировать, если разблокировано org 100h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 1 ; Так как 1-я команда ПП TABLE_1 “лежит”
;;; movwf PCLATH ; в блоке 1, то выбор блока 1.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 200h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 2 ; Так как 1-я команда ПП TABLE_1 “лежит”
;;; movwf PCLATH ; в блоке 2, то выбор блока 2.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 300h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 3 ; Так как 1-я команда ПП TABLE_1 “лежит”
;;; movwf PCLATH ; в блоке 3, то выбор блока 3.
;----------------------------------------------------------------------------------------------------
; Примечание: прежде чем выводить данные на индикацию, нужно, тем или иным способом их заиметь.
; Обычно, результат “оседает” в N-байтном регистре (в двоичном виде. В детали не вдаюсь).
; Далее, содержимое N-байтного регистра нужно “протащить” через соответствующую ПП 2_10
; преобразования (варианты “расписаны” в статье “ПП 2_10 преобразований”), но в данном случае,
; во избежание усложнения текста программы, результаты работы ПП BIN2_10 сымитированы (кстати,
; слово “сымитирован” пишется не через “и”, а через “ы”, а слово “имитация” пишется через “и”),
; например, так (можно и по-другому):
;----------------------------------------------------------------------------------------------------
; Имитация результатов работы ПП BIN2_10.
;----------------------------------------
movlw 1 ; В регистр LED7
movwf LED7 ; записано число 1, что соответствует символу “1”.
movlw 2 ; В регистр LED6
movwf LED6 ; записано число 2, что соответствует символу “2”.
movlw 3 ; В регистр LED5
movwf LED5 ; записано число 3, что соответствует символу “3”.
movlw 4 ; В регистр LED4
movwf LED4 ; записано число 4, что соответствует символу “4”.
movlw 5 ; В регистр LED3
movwf LED3 ; записано число 5, что соответствует символу “5”.
movlw 6 ; В регистр LED2
movwf LED2 ; записано число 6, что соответствует символу “6”.
movlw 7 ; В регистр LED1
movwf LED1 ; записано число 7, что соответствует символу “7”.
movlw 8 ; В регистр LED0
movwf LED0 ; записано число 8, что соответствует символу “8”.
;----------------------------------------------------------------------------------------------------
; В данном случае, результаты 2_10 преобразования “оседают” в регистрах LED7..LED0, что соответствует
; дальнейшему выводу на индикацию показания 12345678 (незначащих нолей нет).
;----------------------------------------------------------------------------------------------------
; Если это нужно, то в дальнейшем можно “протащить” результаты 2_10 преобразования через программную
; процедуру гашения незначащих нолей (можно с ней или без неё. Это зависит от желания конструктора).
; Принцип гашения прост: символ незначащего “0” программно заменяется на символ “пробел” (или
; “пусто”. Обычно, крайний правый ноль не гасится, но можно и загасить или сделать так, как хочется).
; Если, взамен любого из “вышележащих” чисел, “настукать” число .10, то можно отследить программный
; “механизм” вывода на индикацию символа “пробел”.
;----------------------------------------------------------------------------------------------------
clrf Flag ; Регистр флагов очищен (подготовка к “бит-регистрированию”
; событий вывода символов на индикацию).
;====================================================================================================
; Процедура вывода на индикацию, в дисплей графического модуля, символов, “привязанных” к числовым
; результатам предыдущей работы.
;====================================================================================================
; Подготовительные операции.
;------------------------------------
nop ; Символизирует управление графическим модулем: выбор кристалла,
; выбор страницы этого кристалла, выбор нужного столбца страницы,

68
; вывод инструкции “Set Address”, в данном случае, в порт В,
; строб под эту инструкцию.
;------------------------------------
; "Администраторская" группа команд.
;------------------------------------
movf LED7,W ; LED7 -> W.
call CIFRA ; Вызов ПП CIFRA.
;---> Возврат по стеку из ПП CIFRA
bsf Flag,7 ; Контроль окончания вывода на индикацию символа в 8-е,
; самое левое знакоместо (необязательная команда).
;------------------------------------
movf LED6,W ;
call CIFRA ; Аналогично, но для LED6.
bsf Flag,6 ; Контроль окончания вывода на индикацию символа в 7-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED5,W ;
call CIFRA ; Аналогично, но для LED5.
bsf Flag,5 ; Контроль окончания вывода на индикацию символа в 6-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED4,W ;
call CIFRA ; Аналогично, но для LED4.
bsf Flag,4 ; Контроль окончания вывода на индикацию символа в 5-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED3,W ;
call CIFRA ; Аналогично, но для LED3.
bsf Flag,3 ; Контроль окончания вывода на индикацию символа в 4-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED2,W ;
call CIFRA ; Аналогично, но для LED2.
bsf Flag,2 ; Контроль окончания вывода на индикацию символа в 3-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED1,W ;
call CIFRA ; Аналогично, но для LED1.
bsf Flag,1 ; Контроль окончания вывода на индикацию символа во 2-е
; знакоместо (необязательная команда).
;------------------------------------
movf LED0,W ;
call CIFRA ; Аналогично, но для LED0.
bsf Flag,0 ; Контроль окончания вывода на индикацию символа в 1-е,
; самое правое знакоместо (необязательная команда).
;------------------------------------------------
; Окончание процедуры вывода данных на индикацию.
;------------------------------------------------
goto $ ; В данном, учебно-тренировочном случае, “мертвяк”,
; а в реале, переход туда, куда нужно
; (с целью продолжения исполнения программы).
;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
; Группа подпрограмм вывода на индикацию символов цифр.
;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
; Подготовительные операции.
;-------------------------------------
CIFRA movwf Mem ; Сохранение, в регистре Mem, содержимого текущего LEDа.
clrf Reg ; Начинаем с верхней строки таблицы вычисляемого перехода.
;====================================================================================================
; Циклическая ПП вывода на индикацию символов цифр.
;====================================================================================================
; Для символа "0".
;-------------------------------------
SNOVA movf Mem,W ; Mem -> W
sublw 0 ; 0 - W = ... (это символ “0” или другой ?)
btfss STATUS,Z ; Результат =0 или не=0 (нужно выводить на
; индикацию символ "0" или не нужно)?
goto $+3 ; Если не=0, то ПП "знакогенератора" символа "0"
; не вызывается (переход в следующую проверку).
call SIMVOL_0 ; Если =0, то вызывается ПП "знакогенератора" символа "0"
;-> Возврат по стеку из ПП SIMVOL_0 ; (после возврата, код текущего столбца “лежит” в W).
goto OBHOD ; Обход остальных проверок (переход в концовку процедуры).
;-------------------------------------
; Для символа "1".
;-------------------------------------
movf Mem,W ;
sublw 1 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_1 ; Переход в "знакогенератор" символа "1".
goto OBHOD ;
;-------------------------------------

69
; Для символа "2".
;-------------------------------------
movf Mem,W ;
sublw 2 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_2 ; Переход в "знакогенератор" символа "2".
goto OBHOD ;
;-------------------------------------
; Для символа "3".
;-------------------------------------
movf Mem,W ;
sublw 3 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_3 ; Переход в "знакогенератор" символа "3".
goto OBHOD ;
;-------------------------------------
; Для символа "4".
;-------------------------------------
movf Mem,W ;
sublw 4 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_4 ; Переход в "знакогенератор" символа "4".
goto OBHOD ;
;-------------------------------------
; Для символа "5".
;-------------------------------------
movf Mem,W ;
sublw 5 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_5 ; Переход в "знакогенератор" символа "5".
goto OBHOD ;
;-------------------------------------
; Для символа "6".
;-------------------------------------
movf Mem,W ;
sublw 6 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_6 ; Переход в "знакогенератор" символа "6".
goto OBHOD ;
;-------------------------------------
; Для символа "7".
;-------------------------------------
movf Mem,W ;
sublw 7 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_7 ; Переход в "знакогенератор" символа "7".
goto OBHOD ;
;-------------------------------------
; Для символа "8".
;-------------------------------------
movf Mem,W ;
sublw 8 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_8 ; Переход в "знакогенератор" символа "8".
goto OBHOD ;
;-------------------------------------
; Для символа "9".
;-------------------------------------
movf Mem,W ;
sublw 9 ;
btfss STATUS,Z ; Аналогично.
goto $+3 ;
call SIMVOL_9 ; Переход в "знакогенератор" символа "9".
goto OBHOD ;
;-----------------------------------------------------
; Для символа "пробел" (под гашение незначащих нолей).
;-----------------------------------------------------
movf Mem,W ;
sublw .10 ; Аналогично.
btfss STATUS,Z ;
return ; Если проверяемое число более .10 (символ не подлежит
; отображению), то вывод на индикацию программно блокируется
; (путём возврата из ПП CIFRA).
call SIMVOL_NO ; Переход в "знакогенератор" символа "пробел".
;-------------------------------------

70
; Концовка.
;-------------------------------------
OBHOD movwf PORTB ; Код текущего столбца копируется в регистр PORTB.
sublw .255 ; .255 – W = ...
btfsc STATUS,Z ; Это .255 или другое число ?
return ; Если результат =0 (т.е. текущий символ выведен на индикацию),
; то переход на работу со следующим символом.
; Если не=0, то далее (продолжение вывода символа на индикацию).
nop ; Символизирует управление графическим модулем: обеспечение
; условия работы с байтами данных, стробирование, (т.е. вывод на
; индикацию), обеспечение условия работы с командами.
incf Reg,F ; Reg + 1 = ... Результат -> Reg (подготовка к “прыжку”
; в следующую строку таблицы вычисляемого перехода).
goto SNOVA ; Переход на начало вывода на индикацию следующего столбца.
;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
; ................................ ; Символизирует
; ................................ : часть программы.
;***************************************************************************************************
end ; Конец программы.

По сути, то же самое, но в приложении к 11-ти таблицам.


Можно привести и множество других, аналогичных примеров командноскоростной
оптимизации, но полагаю, что “вышележащего” вполне достаточно для принятия
разумного решения о “въезде в её сермяжную суть” (даже “невооружённым глазом видна
симпатичность”).

Использование вычисляемых переходов с целью выборов сценариев


дальнейшей работы программы

Смысл этой вычисляемопереходной функциональности “заложен” в заголовке.


Многие программы работают в зависимости от условий.
Если имеются только 2 условия, то это банально “разруливается” с помощью команд
ветвления и “заморачиваться вычисляемопереходными делами” просто нецелесообразно
(хотя и можно).
Если имеются 3 условия, то это уже “гибридный” случай (можно поступить и так, и эдак).
Если имеются 4 условия и более, то во имя обеспечения компактности процедуры
выбора текущего сценария, целесообразно “заморочиться” вычисляемыми переходами.
Например, 11 сценариев (к этому числу я “привязался” ранее. Пусть оно и будет “фигурировать”).
Если процедуру выбора текущего сценария “ваять” на основе команд ветвления, то
получится громоздкая (по количеству команд) процедура, а если “ваять” на основе
вычисляемого перехода, то получится красатулька, “сермяжный смысл” которой можно
уяснить, поработав (в симуляторе) с этой учебно-тренировочной программой (а кому не лень,
тот пусть попробует “сваять” то же самое, но на основе команд ветвлений):

Программа №16
;***************************************************************************************************
; Программа, позволяющая проверить работу вычисляемого перехода.
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;----------------------------------------
; "Прописка" регистров общего назначения.
;----------------------------------------
cblock 20h ; Назначение адреса первого регистра блока.
Reg ; Регистр выбора сценария.
endc ; Конец блока.
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; ВНИМАНИЕ! ПРИ ПРОВЕРКЕ, ДОЛЖНА РАБОТАТЬ ТОЛЬКО ОДНА ИЗ “НИЖЕЛЕЖАЩИХ” ДИРЕКТИВ org ...
; ОСТАЛЬНЫЕ ДОЛЖНЫ БЫТЬ ЗАБЛОКИРОВАНЫ + ВЫПОЛНЕНИЕ “НИЖЕЛЕЖАЩЕЙ” ...!!!! ИНСТРУКЦИИ !!!...
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; Варианты “безбяковой” работы.
; Можно назначить и другие PC-адреса, лишь-бы не выйти за “границы” блока.
;-----------------------------------------------------------------------------------------------
;;; org 0 ; Первая команда программы “дислоцируется” в блоке 0.
;;; org 100h ; Первая команда программы “дислоцируется” в блоке 1.
;;; org 200h ; Первая команда программы “дислоцируется” в блоке 2.
;;; org 300h ; Первая команда программы “дислоцируется” в блоке 3.
;-----------------------------------------------------------------------------------------------
71
; Варианты “бяковой” работы: “межблочная граница” проходит через таблицу вычисляемого перехода
; (см. окно Program Memory). При желании, её можно адресно сместить выше или ниже заданного
; положения.
;-----------------------------------------------------------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 0-го и 1-го блоков.
;---------------------------------------------
org 0F4h ; Текст программы адресно смещён так, что верхняя часть
; таблицы “лежит” в блоке 0, а нижняя её часть, в блоке 1.
;---------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 1-го и 2-го блоков.
;---------------------------------------------
;;; org 1F2h ; Текст программы адресно смещён так, что верхняя часть
;;; ; таблицы “лежит” в блоке 1, а нижняя её часть, в блоке 2.
;---------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 2-го и 3-го блоков.
;---------------------------------------------
;;; org 2F2h ; Текст программы PC-смещён так, что верхняя часть
;;; ; таблицы “лежит” в блоке 2, а нижняя её часть, в блоке 3.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************

; ................................ ; Часть программы.


;***********************************************************************************************
; Начало программы.
;***********************************************************************************************
START nop ; NOP символизирует некие нужные, подготовительные операции.
;--------------------------------------------------------------------------------
; Имитация выбора сценария (одного из 11-ти).
; Выбирается 5-й сценарий дальнейшей работы программы, но можно выбрать и другой
; (в данном случае, в числовом/”прыжковом” диапазоне от .0 до .10 включительно).
;--------------------------------------------------------------------------------
movlw 5 ; В регистр Reg записывается число 5,
movwf Reg ; что соответствует выбору SCENARIJ_5.
;===================================================================================================
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИНСТРУКЦИЯ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;===================================================================================================
; Если разблокировано org 0 или org 0F4h, то заблокировать всё то, что ниже выделено красным цветом.
;---------------------------------------------------------------------------------------------------
; Разблокировать, если разблокировано org 100h или org 1F2h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 1 ; Так как 1-я команда таблицы “лежит”
;;; movwf PCLATH ; в блоке 1, то выбор блока 1.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 200h или org 2F2h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 2 ; Так как 1-я команда таблицы “лежит”
;;; movwf PCLATH ; в блоке 2, то выбор блока 2.
;-----------------------------------------------------------
; Разблокировать, если разблокировано org 300h
; Остальное (то, что выделено красным цветом) заблокировать.
;-----------------------------------------------------------
;;; movlw 3 ; Так как 1-я команда таблицы “лежит”
;;; movwf PCLATH ; в блоке 3, то выбор блока 3.
;-----------------------------------------------------------
; Подготовка к выбору сценария.
;------------------------------------
movf Reg,W ; Reg -> W.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Группа команд выбора сценариев дальнейшей работы программы (таблица).
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
addwf PCL,F ; PCL + W = ...
goto SCENARIJ_0 ; Безусловный переход в сценарий 0 (W=00000000/.0).
goto SCENARIJ_1 ; Безусловный переход в сценарий 1 (W=00000001/.1).
goto SCENARIJ_2 ; Безусловный переход в сценарий 2 (W=00000010/.2).
goto SCENARIJ_3 ; Безусловный переход в сценарий 3 (W=00000011/.3).
goto SCENARIJ_4 ; Безусловный переход в сценарий 4 (W=00000100/.4).
goto SCENARIJ_5 ; Безусловный переход в сценарий 5 (W=00000101/.5).
goto SCENARIJ_6 ; Безусловный переход в сценарий 6 (W=00000110/.6).
goto SCENARIJ_7 ; Безусловный переход в сценарий 7 (W=00000111/.7).
goto SCENARIJ_8 ; Безусловный переход в сценарий 8 (W=00001000/.8).
goto SCENARIJ_9 ; Безусловный переход в сценарий 9 (W=00001001//.9).
goto $ ; Исполнительная часть последнего сценария типа “программа
; ................................ ; исполняется далее” (W=00001010/.10). В данном случае, это
; ................................ ; “мертвяк”, который символизирует часть программы, алгоритм
; ................................ ; исполнения которой зависит от задумки конструктора.
;------------------------------------------------------------------------------------------------

72
; Исполнительные части сценариев.
; В данном случае, они организованы в виде компактной группы, но она может быть “перебазирована”
; и в другое “место”. И т.д. (вплоть до “разбросанности” исполнительных частей сценариев по тексту
; программы).
;------------------------------------------------------------------------------------------------
SCENARIJ_0
goto $ ; Исполнительная часть сценария 0. В данном случае, это
; ................................ ; “мертвяк”, который символизирует часть программы, алгоритм
; ................................ ; исполнения которой зависит от задумки конструктора.
SCENARIJ_1
goto $ ; Исполнительная часть сценария 1.
; ................................ ; ----“----
; ................................ ; ----“----
SCENARIJ_2
goto $ ; Исполнительная часть сценария 2.
; ................................ ; ----“----
; ................................ ; ----“----
SCENARIJ_3
goto $ ; Исполнительная часть сценария 3.
; ................................ ; ----“----
; ................................ ; ----“----
SCENARIJ_4
goto $ ; Исполнительная часть сценария 4.
; ................................ ; ----“----
; ................................ ; ----“----
SCENARIJ_5
goto $ ; Исполнительная часть сценария 5.
; ................................ ; ----“----
; ................................ ; ----“----
SCENARIJ_6
goto $ ; Исполнительная часть сценария 6.
; ................................ ; ----“----
; ................................ ; ----“----
SCENARIJ_7
goto $ ; Исполнительная часть сценария 7.
; ................................ ; ----“----
; ................................ ; ----“----
SCENARIJ_8
goto $ ; Исполнительная часть сценария 8.
; ................................ ; ----“----
; ................................ ; ----“----
SCENARIJ_9
goto $ ; Исполнительная часть сценария 9.
; ................................ ; ----“----
; ................................ ; ----“----
;-----------------------------------------------------------------------------------------------
; По окончании отработки любого из сценариев (или их группы. Зависит от замысла конструктора),
; должны быть обеспечены условия, обеспечивающие дальнейшее, циклическое исполнение программы.
;-----------------------------------------------------------------------------------------------
; ................................ ; Часть программы.
;***********************************************************************************************
end ; Конец программы.

ВНИМАНИЕ! Блочная специфика никуда не пропала, а имеется в наличии.


По аналогии с “вышележащими” программами, Вы можете “просканировать” как
“безбяковые” варианты работы, так и “бяковые” (см. инструкции, имеющиеся в тексте программы).
В части касающейся “бяковой” работы, в этой программе, PC-смещение задано так, что
“глюк” Вы увидите (в симуляторе. В окне Program Memory. В виде “прыжка” не туда, куда нужно) при
попытке вызова 6-го сценария, а также и сценариев с номерами более 6 (до 5-го сценария
включительно, “глюков” не будет).
В части касающейся “безбяковой” работы, ни в каком из случаев, “глюков” не будет.
Во имя разнообразия, а также и с целью “привязки к насущной насущности” (извините за
тавтологию), рассмотрим вариант применения “сермяжной сути вышележащего” к
двухкнопочному выбору сценариев (2-кнопочная клавиатура. 4 комбинации), что позволяет
пользователю выбрать ту или иную, из вожделеемых им, функций устройства.
Нормально разомкнутые кнопки подключены между выводами RB0 (кнопка №1), RB1
(кнопка №2) и корпусом.
Внутренняя подтяжка порта В (для тех его выводов, которые работают как цифровые каналы ввода)
включена (внешней подтяжки нет).
Программа выглядит так:

Программа №17
;***************************************************************************************************
; Программа, позволяющая проверить работу вычисляемого перехода.

73
; В симуляторе, установлена частота кварца 4 Мгц. (1м.ц. = 1мкс.).
;***************************************************************************************************
LIST p=16f84A ; Используется PIC16F84A.
#include <p16f84a.inc>; Подключение INC-файла PIC16F84A.
errorlevel 2 ; Блокировка вывода сообщений, предупреждений
; (вывод только ошибок).
__CONFIG 03FF1H ; Бит защиты выключен, WDT выключен, XT - генератор.
;-----------------------------------------------------------------------------------------------
org 0 ; Первая команда программы “дислоцируется”
; в PC-ячейке с адресом 0000h.
goto START ; Переход в ПП START.
;***********************************************************************************************

; ................................ ; Часть программы.


;***********************************************************************************************
; Начало программы.
;***********************************************************************************************
START
; ................................
bsf STATUS,RP0 ; Переход в 1-й банк.
bcf OPTION_REG,7; Включение внутренней подтяжки порта В.
movlw 3 ; RB1 и RB0 работают “на вход”, а RB7...RB2 работают “на выход”,
movwf TRISB ; но можно и по-другому. Второе не важно. Важно первое
; (направления работы RB1 и RB0).
bcf STATUS,RP0 ; Переход в 0-й банк.
; ................................
;-----------------------------------------------------------------------------------------
; Это то, что должно происходить в реале, но ... (см. то, что ниже выделено синим цветом).
;-----------------------------------------------------------------------------------------
movf PORTB,W ; Опрос порта В. То есть копирование числа, присутствующего на
; выводах порта B (на выводах, а не на выходах “защёлок”!!!),
; в аккумулятор (W).
andlw 3 ; “Нейтрализация” битов №7...№2, после чего нужно
; уйти в вычисляемый переход.
;-----------------------------------------------------------------------------------------------
; ... , но в связи с дискомфортностью симуляции внешних воздействий (хотя, если у Вас есть такое
; желание, то можно “заморочиться” и функциями стимула), произведена простенькая, “уловочная”
; имитация выбора сценария (одного из 4-х).
; Выбирается SCENARIJ_2, но можно выбрать и другой (в данном случае, в числовом/”прыжковом”
; диапазоне от .0 до .3 включительно).
;--------------------------------------------------------------------------------
movlw 2 ; В W записывается число 2, что соответствует выбору SCENARIJ_2.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Группа команд выбора сценариев дальнейшей работы программы (таблица).
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
addwf PCL,F ; PCL + W = ...
goto SCENARIJ_0 ; Безусловный переход в сценарий 0 (W=00000000/.0)
; (обе кнопки нажаты).
goto SCENARIJ_1 ; Безусловный переход в сценарий 1 (W=00000001/.1)
; (кнопка №1 нажата, а кнопка №2 отжата).
goto SCENARIJ_2 ; Безусловный переход в сценарий 2 (W=00000010/.2).
; (кнопка №1 отжата, а кнопка №2 нажата).
goto $ ; Исполнительная часть последнего сценария “обе кнопки отжаты”
; ................................ ; ” (W=00000011/.3). В данном случае, это “мертвяк”, который
; ................................ ; символизирует часть программы, алгоритм исполнения которой
; ................................ ; зависит от задумки конструктора.
;------------------------------------------------------------------------------------------------
; Исполнительные части сценариев.
; В данном случае, они организованы в виде компактной группы, но она может быть “перебазирована”
; и в другое “место”. И т.д. (вплоть до “разбросанности” исполнительных частей сценариев по тексту
; программы).
;------------------------------------------------------------------------------------------------
SCENARIJ_0
goto $ ; Исполнительная часть сценария 0. В данном случае, это
; ................................ ; “мертвяк”, который символизирует часть программы, алгоритм
; ................................ ; исполнения которой зависит от задумки конструктора.
SCENARIJ_1
goto $ ; Исполнительная часть сценария 1.
; ................................ ; ----“----
; ................................ ; ----“----
SCENARIJ_2
goto $ ; Исполнительная часть сценария 2.
; ................................ ; ----“----
; ................................ ; ----“----
;-----------------------------------------------------------------------------------------------
; По окончании отработки любого из сценариев (или их группы. Зависит от замысла конструктора),
; должны быть обеспечены условия, обеспечивающие дальнейшее, циклическое исполнение программы.
;-----------------------------------------------------------------------------------------------
; ................................ ; Часть программы.
;***********************************************************************************************
end ; Конец программы.

74
В эту программу я не стал вносить “бяковость”, по той причине, что вероятность
“прохождения межблочной границы”, через эту “слабосильную” (в смысле малого количества
строк. Вероятность “безбякового” везения высока, но “на каждую старуху есть проруха”. Это как “приспичит”)
таблицу, мала, но любопытствующие могут её (“бяковость”) внести самостоятельно, по
аналогии с предыдущими программами (см. то, что в них выделено красным цветом).
Результат опроса клавиатуры сымитирован, но в реале, такой имитации быть не
должно, так как нужно работать именно с кнопками, а не с программными “уловками”.
Обратите внимание на то, что я назвал нейтрализацией (это название - моя “самодеятельность”).
Нейтрализация необходима в том случае, если считывается байт (можно работать и побитно,
но выгоднее работать с байтом, так как это более “компактно”) , в котором, под опрос клавиатуры,
задействована часть его битов (те, что “помладше”), а остальная часть битов (те, что
“постарше”), если не предпринять соответствующих усилий, может “изнасиловать
прыжковое число” так, что рабочая точка программы, “не выдержав такого позора,
сиганёт туда, куда Макар телят не гонял” (“глюк”. И про Анну Каренину вспомнил).
Во избежание этого явного, “могильного” недоразумения, перед “PC-прыжком”,
в “битах-насильниках” нужно выставить ноли, что неумолимо приведёт “прыжковое
число к материнскому (безглючному) виду”.
Это осуществляется с помощью логической, байт-ориентированной операции “И”
считанного байта и заданной конструктором константы (команда andlw).
Принцип нейтрализации относительно прост: если тот или иной бит 8-битной константы
= 0, то этот же (с тем же номером) бит результата операции “И”, при любом “раскладе”,
будет = 0 (вот Вам и “нейтрализация”), а если он = 1, то этот же (с тем же номером) бит
результата операции “И”, будет “клоном” соответствующего бита ранее считанного
байта.
В конечном итоге, изначально считанное просто программно “подгоняется под
гарантированно безглючную, вычисляемопереходную работу”.
Если “межблочная граница” проходит через таблицу вычисляемого перехода, то
можно/нужно “подпрячь”, под это дело, операторы high и low, методика “подпряга”
которых описана выше (менее выгодно), либо PC-сместить таблицу таким образом, чтобы
“глюка” не было (более выгодно).

Возможны дополнения и корректировки.

Текущая работа 2011 года        http://ikarab.narod.ru         E-mail: karabea@lipetsk.ru

75