Листинг 5: тест фотоэлементов
; – – – проверка двух младших битов порта ФЭЛ на 0
AND 3 ; наложить на него маску 000000112
LD E, 3 ;
JP NZ, E_ROM ; если не ноль, то ошибка
… ; иначе продолжаем тесты
Инициализация программируемого контроллера прерываний
Для инициализации контроллера надо переслать ему два управляющих слова ICW1 и ICW2, первое по адресу 0CН (A0=0), второе – по адресу 0DН (A0=1).
Пересылаются следующие управляющие слова:
Рис. 6 Применяемые управляющие слова инициализации ПКП
Слово ICW1 установит одиночный (без каскадного соединения) режим работы ПКП (бит 1), 4-хбайтный интервал для начальных адресов обработчиков прерываний (бит 2).
Биты 5–7 слова ICW1 вместе со всем словом ICW2 сообщат контроллеру, что первый обработчик (запроса IR0) начинается с адреса 0020Н.
Контроллер накладывает определенные ограничения на расположение обработчиков в памяти. Первое из них в том, подпрограммы обработки прерываний должны располагаться по порядку, начиная с адреса обработчика запроса IR0, и с постоянным интервалом, т.е. образовывать в памяти таблицу. Интервал расположения может составлять 4 байта (если бит 2 ICW1 равен единице, как в нашем случае), или 8 байт (если бит 2 равен нулю). При постоянном интервале адреса всех обработчиков определятся расположением первого (IR0).
Адрес первого обработчика составляется из полного слова ICW2 (старший байт) и битов 7,6,5 слова ICW1 (старшие три бита младшего байта адреса).
В нашем случае старший байт равен 00Н, младший байт равен 0010 0000 = 20Н. Обработчики имеют начальные адреса: 0020H, 0024H, 0028H, 002CH, 0030H, 0034H (6 обработчиков).
Процедура INI_PIC инициализирует контроллер.
Листинг 6: инициализация программируемого контроллера прерываний
; – – – переслать 38H в порт 0CH и 0 в порт 0DH
INI_PIC LD A, #38 ;
OUT (#0C), A ;
XORA ;
OUT (#0D), A ;
Инициализация переменных системы
В системе за некоторыми ячейками памяти закреплена функция хранения переменных. Например, подсчитанный суммарный объем древесины VS в двоичном формате хранится в отдельных двух байтах памяти с адресами V_SUM, V_SUM+1.
Требуют инициализации только две переменные (инициализируются нулем):
VS (2 байта) – начальный адрес V_SUM;
Время (4 байта) – начальный адрес TIME.
Ясно, что эти имена (V_SUM и TIME) лишь условные обозначения (также, как, например, имена меток в листингах программ). При переводе в машинный код эти имена транслируются в двухбайтный адрес.
Листинг 7: инициализация ячеек суммарного объема и времени
; – – – начальное обнуление объема и времени
INI_VARXORA ;
LD (V_SUM), A ;
LD (V_SUM+1), A ;
LD (V_SUM+2), A ;
LD (TIME), A ;
LD (TIME+1), A ;
LD (TIME+2), A ;
LD (TIME+3), A ;
… ; следует продолжение переходит к основному циклу работы (описание см. в п.4.4).
Арифметические подпрограммы
Микропроцессорной системе необходимо “уметь” выполнять арифметические операции: сложение, вычитание, умножение, деление и косинус. В этом же разделе приведем и неарифметические процедуры для осуществления индикации: преобразования данных в двоично-десятичный код и в семисегментный.
Основным форматом чисел в МП системе является двухбайтный формат с фиксированной точкой вида 1байт , 1байт . Формат беззнаковый, предполагается, что числа положительны. Одно число умещается в одной регистровой паре. Минимальное представимое число – 0,01H=1/256=3,9×10-3, максимальное – FF,FFН=256,996.
Общие правила для всех вычислительных процедур:
- программист вызывающей программы сам следит, чтобы операнды и результат не выходили за пределы представления и чтобы при вычитании не образовалось отрицательных величин;
- операнды при сложении, вычитании, умножении, делении хранятся в регистровых парах HL и DE, результат в HL. Процедура косинуса получает операнд и выводит результат в паре HL.
Сложение
В микропроцессоре Z80 есть команда сложения регистровых пар ADD HL, DE. Однако, чтобы сохранить единообразие, оформим ее все же как подпрограмму.
Листинг 8: подпрограмма PLUS
; – – – подпрограмма сложения
; HL+DE®HL
; сохраняет A, BC, DE
PLUS ADD HL, DE ;
RET ;
Вычитание
В Z80 есть команда вычитания регистровых пар с учетом переноса SBC HL, DE. Используем ее в подпрограмме вычитания, обнулив прежде флаг CY.
Листинг 9: подпрограмма MINUS
; – – – подпрограмма сложения
; HL–DE®HL
; сохраняет A, BC, DE
MINUS OR A ; A не меняется, но флаг CY=0
SBCHL, DE ;
RET ;
Умножение
При умножении первый множитель хранится в паре BC (скопируем его туда из HL в начале), второй множитель хранится в DE. Результат первоначально получается в четырехбайтном виде HL.DE, затем “обрезается” до H.L.
Результат произведения накапливается в HLDE. В течение 16 циклов сдвигов HLDE второй множитель DE постепенно выдвигаясь, уходит вправо и на его место приходит из HL готовые биты произведения. Они получаются при суммировании первого множителя (BC) и левой половины накопленной суммы HLDE (HL). Суммирование делается в случае, если перед этим при сдвиге HLDE вправо из DE был выдвинут единичный бит.
Листинг 10: подпрограмма MUL
; – – – подпрограмма умножения
; HL´DE®HL
; все регистры меняются
MUL LD B, H ; BC=1-й множитель
LD C, L ;
LD HL, 0 ; HL=0
LD A, 16 ; счетчикцикла
MUL2 SRL H ; сдвиг 0®HLDE®CY
RRL ;
RRD ;
RR E ;
JR NC, MUL1 ; если выдвинут 0, то на конец цикла
ADD HL, BC ; если выдвинута 1, то сложить
MUL1 DEC A ;
JR NZ, MUL2 ;
LD H, L ; получено произведение HL.DE
LD L, D ; которое “обрезаем” до L.D и переносим в H.L
RET
Деление
При делении делимое хранится в HL, делитель в DE. Результат получается в виде трех байт [стек.L] (где “стек” – содержимое вершины стека), в конце программы младший байт стека переносится в H и окончательный результат имеет формат H.L. Регистровая пара BC в начале обнуляется.
Деление производится в течение 24-х циклов. В каждом цикле делается сдвиг BC¬HL¬0, т.е. делимое HL выдвигается влево в пару BC. Затем BC сравнивается с DE (путем вычитания BC–DE и проверки флага переноса). Если BC>DE, то младший разряд HL устанавливается единицей, в противном случае он остается нулем. Это один из разрядов частного.
После 16 сдвигов в делимое HL полностью сдвинется влево и на его место придет частное целочисленного деления, в BC будет остаток деления, DE сохранит делитель. На этом этапе целая часть частного в HL заносится в стек, а HL обнуляется. В оставшихся 8-ми сдвигах BC¬HL в BC будут сдвигаться только нули. После всех 24-х циклов в регистре L будет дробная часть частного, а в стеке целая часть.
Листинг 11: подпрограмма DIV
; – – – подпрограмма деления
; HL/DE®HL
; сохраняет DE, использует стек (2 байта)
DIVLDBC, 0 ;
LD A, 24 ; А – счетчик циклов
DIV4 PUSH AF ; сохранить счетчик А в стеке
ADD HL, HL ; BC¬HL¬0
RL C ;
RL B ;
LD A, C ;
SUB E ;
LD C, A ;
LD A, B ;
SBC D ;
LD B, A ;
JR NC, DIV1 ; если BC>DE, топереход
LD A, C ;ADD E ;
LD C, A ;
LD A, B ;
ADC D ;
LD B, A ;
JR DIV2 ;
DIV1 INC HL ; если BC>DE: установить последний бит
; частного в 1
DIV2 POP AF ; извлечь счетчик А из стека
CP #09 ; проверить, 16-й цикл идет или нет
JRNZ, DIV3 ;
PUSH HL ; если 16-й цикл, то в HL целая часть частного,
; сохранить ее в стеке, чтобы извлечь
; в конце программы
LD HL, 0 ;
DIV3 DECA ;
JR NZ, DIV4 ; конец цикла
POP BC ; извлекаем из стека целую часть частного
LD H, C ; получаем частное в H.L
RET ;
Косинус
Чтобы вычислить косинус, используется ограниченный четырьмя членами ряд Тейлора:
, угол a изменяется от 0 до p/2. (6)Эта формула дает косинус с максимальной погрешностью 8,9×10-4, это меньше, чем ошибка разрядности используемого формата [1].[1] дробных чисел.
(7)Представление (7) формулы (6) гораздо удобнее для вычисления косинуса программно. Достаточно сделать процедуру для вычисления
, где n – целое число, b – дробное число. Перед обращением к этой процедуре (названной COS_A) операнды содержатся:HL = a2;
DE = b;
BC = n в формате n.0 (B=n, C=0).
Результат – в HL
Эта подпрограмма использует предыдущие арифметические программы.
Листинг 12: подпрограмма COS_A
; – – – вспомогательная подпрограмма для косинуса
; операнды: HL, DE, BC
; изменяются все регистры
COS_A PUSH BC ; сохранить в стеке n перед вызовом MUL
CALL MUL ; HL=HL´DE
POP DE ; DE=n
CALL DIV ; HL=HL/DE
LD D, 1 ; DE=1.0
LD E, 0 ;
EX HL, DE ;
CALL MINUS ; HL=HL-DE
RET ;
Подпрограмма косинуса вычисляет косинус угла в паре HL.
Листинг 13: подпрограмма COS
; – – –подпрограмма косинуса
; операнд и результат в HL
; изменяются все регистры
COS LD D, H ;
LD E, L ;
CALL MUL ; HL=a2
PUSH HL ; a2встеке
LD D, 1 ; DE=1.0
LD E, 0 ;
LD B, #1E ; BC=1E.0H=30
LD C, 0 ;
CALL COS_A ; HL=1-a2/30
EX HL, DE ;
POP HL ; HL=a2
PUSH HL ; a2встеке
LD B, #0C ; BC=C.0H=12
LD C, 0 ;
CALL COS_A ; HL=1-a2/12(1-a2/30)
EX HL, DE ;
POP HL ; HL=a2
LD B, #02 ; BC=2.0
LD C, 0 ;
CALL COS_A ; HL=1-a2/2[1-a2/12(1-a2/30)]
RET ;
Преобразование двоичный®двоично-десятичный код
В управляющей программе надо переводить двухбайтные числа формата [12].[34] в двоично-десятичный код вида [123.45] размером в пять тетрад для последующего перевода в семисегментный код и индикации.
Для этого нужны две отдельных подпрограммы: одна (по имени B2D) для перевода целой части числа [12] и вторая (имя B2D_F) для перевода дробной части числа [34].