Проблема возникает из-за того, что изменение регистра PCL командой addwf PCL,f затрагивает только 8 младших битов 13-битного счетчика команд. Если при сложении произойдет переполнение, то в итоге счетчик команд изменится в обратном направлении! Например, если подпрограмма из Программы 6.6 будет расположена по адресу h’1F8’ (т. е. метка SVN_SEG будет соответствовать константе h’1F8’) и если в регистре W будет записано число h’08’, то в результате выполнения команды addwf PCL,f в счетчике команд вместо значения h’200’ окажется значение h’(1)F8’ + h’08’ = h’(1)00’. Весьма сомнительно, чтобы команда, расположенная по адресу h’100’, оказалась командой возврата из подпрограммы, поэтому выход из подпрограммы будет произведен некорректно и состояние стека останется несбалансированным. Точное положение подпрограммы в памяти программ предсказать нелегко, поскольку вряд ли программист может заранее сказать, в каком месте памяти программ будет расположена подпрограмма, т. е. какое значение будет в РС при входе в подпрограмму. Даже если он узнает значение SVN_SEG, просмотрев ассемблерный листинг (см. Листинг 8.2 на стр. 247), оно может впоследствии измениться в результате корректировки других частей программы. Немного усложнив программу, ее можно сделать нечувствительной к пересечению этой 256-байтной границы (см. Программу 6.7).
Хранение данных с использованием последовательности команд retlw довольно неэффективно, поскольку 14-битное слово используется для хранения 8-битного значения. В микроконтроллерах линейки PIC16F87X реализована возможность чтения 14-битных данных непосредственно из памяти программ, правда, достаточно «криво» (см. Программу 15.5 на стр. 553). Микроконтроллеры старшего семейства имеют специальные команды, такие как tblrd, которые позволяют обращаться к отдельному байту любого 16-битного слова памяти программ (см. Табл. 16.1 на стр. 585).
Использование W для передачи данных в/из подпрограмм ограничено одним байтом в каждом направлении. Если необходимо передать несколько однобайтных значений или значение большей разрядности, то для этой цели придется задействовать регистры данных. В качестве примера рассмотрим подпрограмму, код которой приведен в Программе 6.7. Эта подпрограмма выполняет перемножение двух однобайтных значений, обозначенных как MULTIPLICAND и MULTIPLIER, и возвращает 16-битное значение PRODUCT_L: PRODUCT_H (Рис. 6.9).
Рис. 6.9.
Алгоритм умножения, реализованный в Программе 6.7, представляет собой обобщенный вариант алгоритма, использованный нами в предыдущих процедурах умножения, например в Программе 5.9, приведенной на стр. 163. В указанном примере значение множителя, равное 9, представлялось в виде суммы (1 + 8). Аналогичным образом, умножение на 10 можно выполнить путем однократного (х2) и троекратного (х8) сдвига исходного значения влево с последующим сложением полученных частичных произведений. В общем случае множимое циклически сдвигается влево и значение, полученное в результате
где символы «<<» обозначают операцию сдвига влево.
Таким образом, в Программе 6.7 реализован следующий алгоритм:
1. Обнулить 2-байтное произведение.
2. Расширить множимое до 16 бит.
3. ВЫПОЛНЯТЬ, ПОКА множитель не станет равным нулю:
а) Сдвинуть множитель вправо.
б) Если есть перенос, то прибавить число, полученное в результате сдвига множимого к 2-байтному частичному произведению.
в) Сдвинуть множимое вправо.
4. Вернуть 16-битное произведение.