Открыть Электронные книги
Категории
Открыть Аудиокниги
Категории
Открыть Журналы
Категории
Открыть Документы
Категории
Оглавление
Память программ
Общие положения
Карта пользовательской памяти программ, с обозначением области
конфигурирования
“Линейная” PC-адресация
Счётчик команд PC
Разделение пользовательской памяти программ на блоки и страницы
Страничные особенности работы
Аппаратный стек
Вычисляемые переходы
Стандартные ПП вычисляемых переходов
Универсальные ПП вычисляемых переходов
Применение, в таблицах вычисляемых переходов, директивы dt
Варианты программных процедур вычисляемых переходов, применяемых при
работе с ЖК-модулями (на основе м/к HD44780).
Стандартные и универсальные ПП вычисляемых переходов
Вариант универсальной подпрограммы вычисляемого перехода с
идентификатором конца таблицы
Варианты программных процедур вычисляемых переходов, применяемых при
работе с графическими модулями (на основе м/к KS0108).
Стандартные и универсальные ПП вычисляемых переходов
Вариант универсальной подпрограммы вычисляемого перехода с
идентификатором конца таблицы
Способы организации вычисляемых переходов в приложении к произвольному
порядку формирования “прыжковых” чисел (для графического модуля на основе м/к KS0108).
Принципы командноскоростной оптимизации процедур вычисляемых переходов
Использование вычисляемых переходов с целью выборов сценариев
дальнейшей работы программы
1
Пояснение: по причине того, что работа со стеком и с вычисляемыми переходами предполагает
“въезд в память программ”, то перво-наперво необходимо разобраться именно с ней.
Память программ
Общие положения
Общепринятая абревеатура памяти программ - PC
(так короче. На рис. 1 выделено светло-голубым цветом).
Память программ состоит из:
пользовательской памяти программ,
области конфигурирования.
Кроме того, память программ “укомплектована” (“на правах обязательной обслуги”)
счётчиком команд PC,
аппаратным стеком.
Пользовательская память программ
Это та область памяти программ, которая доступна пользователям (то есть, программистам /
конструкторам / любопытствующим /...).
Проще говоря, это то, что отображается в окне MPLAB с названием Program Memory.
Пользовательская память программ состоит из двух частей: активной и пассивной
(за исключением “стёртого ПИКа”. В этом случае есть только одна часть - пассивная) .
2
Область конфигурирования
Область конфигурирования “дислоцируется ниже нижней границы” пользовательской
памяти программ (адреса 2000h ... 3FFFh).
В ПИКах базового и среднего семейства, обращения к содержимому области
конфигурирования возможны только в режиме программирования ПИКа.
Примечание: в ПИКах 18-й серии гораздо симпатичнее, так как конструктор, по ходу исполнения программы, имеет
возможность изменять параметры конфигурации.
3
Как видите, по максимуму, PC-возможности реализованы в PIC16F876/876A,
PIC16F877/877A, а в других указанных типах ПИКов, они используются частично.
Обратите внимание на то, что в пользовательской памяти программ (она ограничена красной
рамкой) закрашено тёмно-серым цветом (с пометкой “Не используется”).
Это физически нереализованные области PC.
Проще говоря, там нет PC-ячеек, а значит и обращаться туда и не нужно (хотя и можно,
но об этом ниже), и бесполезно.
Физически реализованная область PC (“рабочая” область PC) состоит из определённого
количества PC-ячеек (в рассматриваемых случаях: 8192, 4096, 2048, 1024. Зависит от типа ПИКа).
Одна PC-ячейка предназначена для энергонезависимого запоминания 14 – битного кода
любой из команд, входящей в систему команд (см. “разрисовку” кодов команд: стр. 29-7 … 29-42
“Справочника по среднему семейству …”).
Примечание: 14-битный код, не являющийся кодом команды, исполняется как “пустой” машинный цикл. То есть,
результат исполнения этой “неправильной” команды такой же, как результат исполнения “правильной” команды NOP.
Это к вопросу о том, откуда возникают “виртуальные” NOPы.
Примечание: В пиках 18-й серии, кроме “прыжковых” команд call, goto, есть ещё и другие “прыжковые” команды.
Счётчик команд PC
Адресными “PC-делами рулит” счётчик команд PC.
Выбор адреса PC-ячейки осуществляется с помощью управляющих регистров PCL
(младший) и PCH (старший).
Регистр PCL доступен для чтения/записи (является регистром спецназначения), а регистр PCH,
хотя и физически реализован, но не доступен для чтения/записи (не является регистром
спецназначения).
В части касающейся регистра PCL, необходимость “присвоения ему статуса” регистра
спецназначения, причём, отображаемого во всех банках, диктуется наличием такой
полезной “штуковины”, как вычисляемый переход (см. ниже), “атрибутом” которого
является программное приращение содержимого регистра PCL (если бы регистром PCL нельзя
было управлять, то и вычисляемых переходов не было бы).
А вот с регистром PCH немного “похитрее”.
Постараюсь объяснить.
Во многих случаях, воздействовать на его содержимое просто нет необходимости, но
есть два исключения:
;*****************************************************************************************
; Доказательство утверждения, сформулированного в пункте 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, необходимость страничной коррекции “отпала” (то есть,
производить её не нужно), но необходимость блочной коррекции не “отпала”, и её суть такая же, как в ПИКах
базового и среднего семейства. Поэтому советую обратить самое пристальное внимание на то, о чём говорится в
этой статье (и особенно на “блочные дела”).
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.
8
В приложении к 8-Килословной (то есть, по максимуму), пользовательской памяти программ
(PIC16F876/876A, PIC16F877/877A), разделение на страницы и блоки выглядит так:
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, и не более того.
А именно:
Таким образом, в части касающейся пункта 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).
Теперь “на повестке дня разборки” с блоками, ведь разделение на блоки чем-то
мотивировано, а иначе зачем “городить этот блочный огород”?
12
Забегая вперёд, скажу, что разделение на блоки обусловлено особенностями
работы программных процедур с названием “вычисляемые переходы”, которые,
во многих случаях (это о “классических” вычисляемых переходах), если так можно выразиться,
являются “симбиозом блочного и стекового”, что, в свою очередь, предполагает
последовательный “въезд” в обе этих “составляющих”.
Начну со стека.
Аппаратный стек
В ПИКах базового и среднего семейства, стек это двунаправлено управляемое
(“загрузка/выгрузка”) “оперхранилище” 13-разрядных, то есть, абсолютных (работающих по
всему адресному массиву пользовательской PC) адресов возвратов.
Сие “оперхранилище”, хотя и не отображается в памяти программ и в памяти данных
(спасибо, что хоть симулятор выручает), но имеется в реале (просто “спрятался за угол”) и выглядит
примерно так (таблица стека выделена светло-голубым цветом):
13
Стек удобно изобразить (или представить себе) в виде таблицы (см. рис. 8).
Так как в ПИКах базового и среднего семейства, стек является 8-уровневым
(или 8-строчным), его таблица состоит из 8-ми строк.
Стек задействуется:
если отрабатывается “связка”: call (программная “загрузка”) ... return/retlw (программная
“выгрузка”).
если отрабатывается “связка”: уход в ПП прерывания (аппаратная “загрузка”) ...
retfie (программная “выгрузка”).
Примечание: указанный выше, аппаратный автоинкремент PC-адреса необходим для того чтобы осуществить
дальнейший, адресный возврат не на ту команду, по окончании отработки которой произошёл вызов вызываемой ПП
или ПП прерывания (в этом случае, будет “жесточайший глюк”), а на следующую, после неё, команду. Этим
обеспечивается продолжение работы программы (после возврата) от этой команды.
14
(в системе команд, нет команды ухода в ПП прерывания) ,
нужно озаботиться только возвратом из
ПП прерывания.
Это означает то, что в состав односценарной (в том смысле, что один сценарий возврата) ПП
прерывания, в обязательном порядке, должна входить команда возврата, а в состав
многосценарной (в том смысле, что несколько сценариев возврата) ПП прерывания, должны
входить несколько (по количеству сценариев возврата) команд возврата.
Несоблюдение этого правила, по сути, приводит к тому же “бардальеро” (см. выше).
Примечание: команды возвратов, если так можно выразиться, “символизируют собой” окончание отработки
односценарных / многосценарных (это о количестве сценариев возврата), вызываемых ПП или ПП прерывания.
Таким образом, “сермяжная суть” работы стека сводится к (если так можно выразиться)
“PC-адресному обеспечению двунаправленной прыгучести”, сначала “туда”, а потом
“сюда”, а в “промежутке”, между этими “туда” и “сюда”, исполняется какая-то нужная,
программная процедура (время её исполнения не лимитировано).
При таком “раскладе”, без “стек-обслуги” не обойтись.
А это “то же самое яйцо, но только в профиль”: по факту осуществления вызова
(вызываемой ПП или ПП прерывания), рабочая точка программы, дождавшись окончания
отработки текущей команды, на время “отлучается в вожделеемое место”, делает, в
этой “отлучке”, какие-то нужные (по замыслу конструктора) дела, а по окончании их “делания”,
возвращается на начало исполнения следующей (относительно той команды, на которую пришёлся
вызов) команды.
Теперь о крайностях.
Крайность №1
Если хотя бы одна команда call циклически исполняемой программы не
“укомплектована” командой возврата (return/retlw), то в конечном итоге произойдёт так
называемое “переполнение стека”, с дальнейшей его работой по кольцу.
То есть, начиная от 9-го адреса возврата и далее, по направлению сверху вниз
(относительно вершины стека), начнётся “смертоубийство” того, что ранее было “добыто тяжким
и непосильным стек-трудом” (запись “по верху”), со всеми вытекающими, “кладбищенскими”
последствиями (они просто отвратительны).
Если, в тексте ПП прерывания, конструктор забыл “настукать” команду retfie, то будет
то же самое.
Вывод: в любом случае, нельзя допускать переполнения стека.
Дело несколько осложняется тем, что отследить содержимое стека можно только в
симуляторе (в MPLAB версии 5.70.40 à окно Stack Window, в современных версиях версии à окно Hardvare Stack).
Проще, уяснив сказанное, не допускать переполнения стека, что, кстати, не так уж и
сложно (просто работа на внимание. Компетентность в этом вопросе - само собой разумеющееся).
Крайность №2
Если в тексте программы имеется лишняя (по причине ненужности/вредоносности), исполняемая
команда возврата, то она будет “паразитно” смещать содержимое стека вверх.
То есть, будут осуществляться преждевременные возвраты.
В итоге, это безобразие может закончиться (если ранее не “глюканёт”) тем, что официально
называется “исчерпанием стека” (а мне больше нравится слово “опустошение”), но только не нужно
путать это “исчерпание” (“бяка”) с другим “исчерпанием” (не “бяка”), которое, с целью
“разграничения полномочий”, целесообразно назвать “очищением” стека.
Если стек-ошибок нет, то неоднократное “очищение” стека - абсолютно нормальное,
“штатное” явление, которое обусловлено тем, что по ходу исполнения программы,
содержимое стека, по причине “загрузки/выгрузки” адресов возвратов (порядок
“загрузки/выгрузки” определяется замыслом программы) , смещается “туда-сюда”.
Примечание: в ПИКах базового и среднего семейства, нельзя программно изменить/прочитать содержимое стека,
15
поэтому он и назван аппаратным стеком.
В ПИКах 18-й серии, он, если так можно выразиться, “гибридный”, а в ПИКах 24-й серии, он программный.
Вычисляемые переходы
Стандартные ПП вычисляемых переходов
Вычисляемый переход это программная, “прыжковая” процедура
считывания содержимого одной из строк таблицы вычисляемого
перехода (“классика”)
или табличного выбора одного из сценариев дальнейшей работы
программы (и так тоже можно).
Это, как говорится, “за один присест”, но по ходу исполнения программы, таких
“присестов” может быть много, причём, не с абы какими, а с целенаправленными
обращениями к содержимому различных строк таблицы вычисляемого перехода.
“Железобетонным атрибутом” программной процедуры вычисляемого перехода является
команда 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 слов (между байтом и словом есть разница).
16
Если не предпринимать мер по выбору соответствующего блока, то в этом случае,
будет адресно выбрана не верхняя ячейка “нижележащего” блока, а ячейка
“вышележащего” блока, с тем адресом (внутри этого блока), который указан в регистре PCL.
Соответственно, можно сформулировать правило:
;-----------------------------------------------------------------------------------------------
; Если есть желание проверить работу этой программы не только в симуляторе, но и в “железе”,
; то разблокируйте выделенное тёмно-зелёным цветом. Это то, что относится к фиксированной
; задержке. Если её не будет, то цифры, выводимые на индикацию, будут сменяться очень быстро.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Вызываемая ПП преобразования двоично-десятичного кода в код 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).
А это для тех, кто пожелает проверить работу этой программы в “железе”:
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 и заблокируете верхнюю, то будет то же
самое, но с другими числовыми значениями (они указаны в тексте программы).
О приятном сказал.
Теперь скажу о неприятном (в жизнеутверждающих/познавательных/упреждающих целях).
Для того чтобы разобраться в неприятном (зачем быть битым, если этого можно избежать?),
а заодно и сравнить его с приятным, “сваял” эту учебно-тренировочную программу:
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.
;***********************************************************************************************
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 ; Конец программы.
“Глюк”, возникающий при смене 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, который, в данном случае, не
осуществляется.
Таким образом, действительности соответствует это:
Примечание: как видите, “правильные” адреса “глюкопрыжков” 0000h, 0100h, 0200h отличаются от “неправильных”
адресов “глюкопрыжков” 0032h, 0132h, 0232h (для MPLAB версии 5.70.40) или 0063h, 0163h, 0263h (для MPLAB
версии 8.10).
22
а нижняя часть, в блоке 1, и рабочая точка программы “глюкопрыгнула” в начало
блока 0.
Это означает то, что вместо симпатичной, плановой реализации задумки конструктора,
“паразитнопринудительно” начнётся программное “оргбардальеро”, последствия которого
могут быть всяческими (это зависит от конкретики “паразитнопринудительно” исполняемой части программы).
Но всё это “мелочь пузатая” по сравнению со “стек-бардальеро”.
Дело в том, что перед “глюком”, в вершину стека “загружается” адрес возврата (так как
call TABLE), а соответствующая команда возврата (в данном случае)
не исполняется.
Получается, что команда вызова, по “паразитнопринудительным” причинам, оказалась
“неукомплектованной” командой возврата.
Если этот “глюк” повторится несколько раз, то произойдёт переполнение стека
(в симуляторе, можно увидеть процесс заполнения таблицы стека, а как “апофеоз” à “стек-предупреждалка”).
С учётом того, что эти “бяки работают в комплексе”, в итоге, речь идёт о
стопроцентном, жесточайшем, функциональном “глюке”, о который, если быть
некомпетентным в этих вопросах, частенько “разбиваются душ прекрасные порывы”.
Надеюсь на то (говорю на полном серьёзе), что “въехав в сермяжную суть этого типа
глючности”, техническая составляющая Ваших душ локально возрадуется (со всеми
вытекающими).
Голос из-за кулис: “Ну и настращал … А процедура-то компактная. В этом смысле,
красатулька/персик/... Как выкрутиться-то, Склифосовский”?
Ответ: привет, дружище! Рад пообщаться. Давно тебя не было. Если объявился,
значит я на верном пути.
На счёт “красатульки/персика/...” никто и не спорит.
Так оно и есть, но этот “персик” должен “дислоцироваться” строго внутри PC-блока и
не “выходить за его границы”, а иначе это будет совсем не “персик”, а обидный
“PC-кукиш”.
Лично я, предпочитаю компактно “валить эти персики” в блок 0, в виде так
называемой (мной) “верхней обслуги”.
Вопрос: “Почему так”?
Ответ: потому, что в этом случае, в процессе создания текста программы, сии
“персики”, в PC, не смещаются вниз, то есть, не изменяют свою “блочность”, а
“дислоцируются” строго в блоке 0.
Если имеется ПП прерывания (она “дислоцируется” почти в начале блока 0), то “персики”
целесообразно разместить ниже её.
Во многих случаях, ПП прерывания не очень массивна” и занимает только часть
блока 0, что позволяет разместить в нём и вызываемые ПП вычисляемых переходов.
Примечание: в этом случае, в идеале, текст ПП прерывания нужно “орготработать” в первую очередь. В том смысле,
чтобы в дальнейшем не изменять количество её команд, а иначе это приведёт к PC-смещению всего “нижележащего”
текста программы (возникает риск прохождения “межблочной границы” через таблицу вычисляемого перехода).
Если же без этого не обойтись (в жизни всякое бывает), то после осуществления любого такого изменения,
необходимо убедиться (в окне Program Memory) в том, что таблицы “нижележащих” ПП вычисляемых переходов не
“наехали” на “межблочные границы”, а если “наехали”, то необходимо, тем или иным способом (сместить,
“передислоцировать, из пункта А, в пункт Б” и т.п.), устранить эту “бяку”.
23
В этом случае, речь идёт об универсальной ПП вычисляемого перехода, которая, если
так можно выразиться, “не боится межблочных границ”, а заодно и любых
“PC-смещений вышележащего”.
То есть, она может “дислоцироваться” в том “месте” текста программы, в которое её
сочтёт нужным поместить конструктор.
Прежде чем толковать о такой разновидности ПП вычисляемого перехода, нужно
разобраться с работой операторов high и low (есть ещё и upper, но в ПИКах базового и среднего
семейства этот оператор не востребован).
Вы знакомы (надеюсь на это) с оператором $.
Операторы high и low тоже “из этой епархии” (см. список арифметических операций, “Руководство
пользователя MPASM”, стр. 51), только со своими, специфическими функциями.
Проще всего разобраться на конкретном примере.
В приложении к “бяковым” вариантам, по сути (без учёта деталей), “нижележащая”
программа является как бы “двойником вышележащей” программы, но с одной
существенной/симпатичной оговоркой: в этих неблагоприятных условиях, программа не
“глючит” (как предыдущая), а нормально работает.
Примечание: “безбяковые” варианты “расписывать” не стал, но если у Вас есть желание убедиться в их
“безглючности”, то по аналогии с “вышележащей” программой, “врежьте” в текст “нижележащей” программы
;;; org 0 ; Первая команда программы “дислоцируется” в блоке 0.
;;; org 100h ; Первая команда программы “дислоцируется” в блоке 1.
;;; org 200h ; Первая команда программы “дислоцируется” в блоке 2.
;;; org 300h ; Первая команда программы “дислоцируется” в блоке 3.
и “поиграйте” с блокировками.
;###############################################################################################
; НАЧАЛО ВЕРХНЕЙ “ОБСЛУГИ” (вариант организации “обслуги”).
;###############################################################################################
; Вызываемая ПП преобразования двоично-десятичного кода в код 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 ; Конец программы.
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:
Вариант 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 удобно (при условии качественного “въезда в
сермяжную суть матери”!!!).
Соответственно, в дальнейшем, я её и буду применять.
Примечание: в таблице вычисляемого перехода, синтаксис чисел может быть таким, каким его назначит конструктор
(это о системах счисления).
Для программ №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 ; Вывод цифры на индикацию.
; ................................
Примечание: для того чтобы облегчить составление кода надписи, Пётр Высочанский придумал программу-конвертор
ConverterForHD44780, архив которой прилагается к этой статье.
После распаковки архива, нужно щёлкнуть по распакованному файлу и согласится с созданием файла, хранящего
данные о профилях. После этого, файл будет создан и откроется окно программы, которое разделено на 2 части.
В верхней части, нужно “настучать” вожделеемые символы, а затем Преобразование à Преобразовать…
После этого, в нижней части окна программы, “возникнет” содержимое таблицы вычисляемого перехода, которое
подлежит копированию в текст программы (можете свериться с таблицей русифицированного знакогенератора).
Надпись ЗАЩИТА ВЫКЛЮЧЕНА занимает 16 знакомест (15 символов букв и символ “пусто” или
“пробел”. Знакоместо - место визуальной “дислокации” адресно выбранного символа),
значит, задействована
вся строка ЖК-дисплея.
Соответственно, нужно начать вывод символов на индикацию с первого знакоместа
выбранной строки (с сАмого левого), а закончить в последнем (в сАмом правом).
В том случае, если не предпринимается “спецмер” по адресному “перескоку” через
N-знакомест (возможны и такие случаи), то по окончании вывода символа на индикацию
(в предыдущее знакоместо) , происходит автоматический выбор следующего знакоместа, что
существенно оргупрощает процесс вывода данных на индикацию.
Таким образом, в рассматриваемом случае, речь идёт не о произвольном порядке
формирования “прыжковых” чисел, а о последовательном, строго заданном
порядке формирования “прыжковых” чисел.
При этом, “PC-странично-блочная” специфика никуда не исчезла и полномасштабно
соблюдается.
Ниже приведён текст программы, который, по сути, является аналогом программы №2
(“инструкцию по эксплуатации” повторять не буду. См. выше), но только в приложении к ЖК-модулю
(на индикацию, в строку ЖК-дисплея, выводится надпись ЗАЩИТА ВЫКЛЮЧЕНА) :
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 или 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 ; Конец программы.
31
;;; ; TABLE “лежит” в блоке 1, а нижняя её часть, в блоке 2.
;---------------------------------------------
; Через таблицу вычисляемого перехода проходит
; “межблочная граница” 2-го и 3-го блоков.
;---------------------------------------------
;;; org 2F2h ; Текст программы PC-смещён так, что верхняя часть ПП
;;; ; TABLE “лежит” в блоке 2, а нижняя её часть, в блоке 3.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************
32
Теперь настало время познакомить Вас с ещё одним вариантом универсальной ПП
вычисляемого перехода, который, на мой взгляд, достаточно удобен/перспективен.
Вопрос: “А нельзя ли обойтись без заморочек с назначением количества прыжков, то
есть, назначить другой критерий окончания отработки таблицы вычисляемого
перехода”?
Ответ: можно, если закончить таблицу тем, что гарантированно не будет выводиться
на индикацию, но будет являться признаком окончания таблицы.
Если рассуждать комплексно (и в приложении к ЖК-модулям, и в приложении к графическим модулям),
то лучше всего “на эту роль подходит” число FFh.
В приложении к ЖК-модулям, это символ типа “чёрный прямоугольник” (все точки знакоместа
активны), востребованность которого очень низка, а в приложении к графическим
модулям, это “чёрный столбец” (все его точки активны).
В последнем случае, необходимо “конструировать” символы таким образом, чтобы
состояний FFh не было.
Вернусь к ЖК-модулям.
Программная реализация сказанного выглядит так (последнее число таблицы вычисляемого перехода,
а именно 0xFF, с целью привлечения внимания, выделено красным цветом):
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 ; Конец программы.
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.
;***********************************************************************************************
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 ; Конец программы.
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 ; Конец программы.
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 ; Конец программы.
Примечание: так как эта статья посвящена вычисляемым переходам, то следует отметить, что в
приложении к случаям произвольного порядка формирования “прыжковых” чисел, ЖК-модули как бы
“выпадают” из её контекста.
Мотивация: в ЖК-модулях, таблица знакогенератора организована аппаратно.
Это означает то, что в приложении к ЖК-модулям, применение вычисляемых переходов целесообразно
только тогда, когда порядок формирования “прыжковых” чисел строго задан (например,
программы №4, 5, 6), а также и то, что в рассматриваемом, “динамоанархическом” случае, востребованы
не вычисляемые переходы, а косвенная адресация. Это отдельный разговор, “привязанный” к
обсуждению деталей того, что связано с оперпамятью.
42
желании, можно задать программно (а в реале, это результаты измерения/подсчёта “того-сего”, причём, в
динамике, но это отдельный разговор).
С целью обеспечения относительного единообразия оформления текстов программ,
тексты “нижележащих” программ “ваялись по образу и подобию вышележащих”
(естественно, что различия, в деталях, есть, но “сермяжная, вычисляемопереходная суть” одна и та же) .
8-столбцовые коды символов цифр 0 … 9 (в данном случае, одно знакоместо состоит из 8-ми столбцов)
формируются с помощью прилагаемого, к этой статье, CHR-файла библиотеки русских
символов (а можно и английских. Цифры везде одинаковы).
Сначала, предлагаю Вашему вниманию “безуниверсальный” (без применения операторов high и
low) вариант, в котором можно отследить как “безбяковую”, так и “бяковую” (в смысле
наличия “глюков”) работу (если Вы желаете стать МАСТЕРОМ, то “сермяжную суть PC-глюков”, а также и
других разновидностей “глюков”, нужно осознать лично !!! Понимаю, что хлопотно, но куда деваться?):
43
; “межблочная граница” 2-го и 3-го блоков.
;----------------------------------------------------
;;; org 2EFh ; Текст программы PC-смещён так, что верхняя часть ПП
;;; ; “знакогенератора” символа “1” (SIMVOL_1) “лежит” в блоке 2,
;;; ; а нижняя её часть, в блоке 3.
;;; ; “Глюк” будет при переходе от 0x84 к 0xFE.
;-----------------------------------------------------------------------------------------------
goto START ; Переход в ПП START.
;***********************************************************************************************
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