Примеры написания программ.

Рассмотрим примеры написания простейших фрагментов, которые могут встретиться в программе. При разборе примеров будем пользоваться условными регистрами: A – аккумулятор; B, C, D, E, H, L – регистры общего назначения, которые могут объединяться в регистровые пары BC, DE, HL. Причем регистровая пара HL будет выполнять функции адресного указательного регистра, т.е. регистра, указывающего на адрес (содержащего адрес) ячейки памяти M, к которой идет обращение в данной команде при косвенной регистровой адресации. Такой синтаксис заимствован из языка Ассемблера МП Intel8080.

Пример 1. Проверить состояние третьего разряда регистра B. Если значение этого разряда равно 1, то выполняется следующая по программе команда. В противном случае выполняется команда, адрес которой 00АА16.

Решение. Для микроконтроллеров, представляющих собой функционально полную МП-систему, реализованную в одном кристалле микросхемы, как правило, характерно наличие в системе команд специальных команд побитовых операций. С их помощью можно выделять определенные биты, записывать в них нули, единицы, инвертировать их значения. Многие МП таких команд не имеют. Но это не значит, что операции над отдельными битами для них неприемлемы. Для выделения каких-либо разрядов имеющегося байта выполняется операция поразрядного маскирования, с помощью которой из рассмотрения исключаются не интересующие нас разряды. В даннмо случае маскирование производится путем поразрядного логического умножения исходного байта на маску, в качестве которой используется набор битов, равных нулю во всех разрядах, кроме проверяемого третьего.

Очевидно, что результат умножения будет равен или не равен нулю в зависимости от того, равно или не равно нулю значение проверяемого разряда исходного байта. Таким образом, представленная задача решается путем выполнения следующей последовательности операторов:

 

Метка Операция Операнд Комментарии
  MVI A,00000100b ;поместить в аккумулятор маску 000001002.
  ANA B ;выполнить поразрядное логическое умножение маски, содержащейся в аккумуляторе и проверяемого регистра B.
  JZ 00AAh ;если результат равен нулю (в третьем разряде регистра B был ноль), перейти к команде с адресом 00AA16.
  --- --- ;выполнение дальнейшей программы в противном случае.

 

Пример 2. Не изменяя значений остальных разрядов, записать единицы в первый и второй разряды, ноль в пятый разряд и изменить на противоположное значение шестого разряда содержимого ячейки памяти с адресом A5C216.

Решение. Очевидно, что при поразрядном логическом сложении исходного байта и маски, значения тех разрядов исходного байта, в которых маска содержит нули, не меняются, а тех разрядов исходного байта, в которых маска содержит единицы, станут равными единице. Для записи нуля в интересующие разряды исходного байта необходимо, как и в предыдущем примере, логически умножить этот байт на маску с нулями в этих разрядах. При выполнении поразрядной логической операции ИСКЛЮЧАЮЩЕЕ ИЛИ над исходным байтом и маской, значения тех разрядов исходного байта, в которых маска содержит нули, не меняются, а значения остальных разрядов изменятся на противоположные.

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

Модификацию необходимо выполнить над содержимым ячейки памяти. Как мы договорились, это содержимое поместим в аккумулятор. В аккумуляторе сформируется и результат последней выполненной команды. Но по условию изменить надо содержимое ячейки памяти, т.е. результат нужно будет из аккумулятора переслать обратно в ячейку памяти. Таким образом, необходимо будет дважды обратиться к одной и той же ячейки памяти. В тех случаях, когда необходимо многократно обращаться к ячейке памяти с одним и тем же адресом подряд, или адрес ячейки памяти меняется по определенному закону, например инкрементируется или дикрементируется при каждом обращении, удобно использовать косвенную регистровую адресацию. Это позволяет повысить время выполнения программы, один раз задав адрес в указательный адресный регистр, а само обращение к памяти выполнять с помощью быстрых однобайтовых команд косвенной адресации. В принятых нами обозначениях такая адресация реализуется путем обращения к ячейке памяти M как к внешнему регистру, адрес которой указывается регистровой парой HL.

Поставленная задача может быть решена следующей последовательностью команд:

 

Метка Операция Операнд Комментарии
  LXI H,0A5C2h ;загрузить регистровую пару HL (определить адрес) адресом A5C216 ячейки памяти.
  MOV A,M ;переслать в аккумулятор содержимое ячейки памяти с адресом A5C216.
  ORI 00000011b ;произвести поразрядное логическое сложение содержимого аккумулятора и маски 000000112 – записать единицы в первый и второй разряды исходного байта.
  ANI 11101111b ;произвести поразрядное логическое умножение содержимого аккумулятора и маски 111011112 – записать ноль в пятый разряд исходного байта.
  XRI 00100000b ;произвести поразрядное логическое «сложение по модулю два» содержимого аккумулятора и маски 001000002 – изменить на противоположное значение шестого разряда исходного байта.
  MOV A,M ;переслать результат модификации, сформировавшийся в аккумуляторе, обратно в ячейку памяти с адресом A5C216.
  --- --- ;продолжение программы.

 

Пример 3. Содержимое предварительно обнуленного регистра B увеличивать на единицу до тех пор, пока оно не станет равным содержимому регистра C, после чего перейти к выполнению дальнейшей программы.

Решение. Очевидно, что для решения поставленной задачи необходимо организовать циклический процесс, на каждом шаге которого будет выполняться инкрементирование содержимого регистра B и сравнение его текущего значения с содержимым регистра C. Как только содержимые этих регистров сравняются, будет осуществлен выход из цикла. Для установления факта равенства необходимо использовать команду сравнения. Пока не будет иметь место равенство, разница содержимых этих регистров будет не нулевой, т.е. признак нуля регистра флагов будет иметь нулевое значение. В этом случае необходимо будет перейти к следующему шагу цикла, сославшись на метку первой команды тела цикла командой условного перехода по отсутствию нулевого результата. Как только команда сравнения даст нулевой результат вычитания содержимых регистров B и C, условие ненулевого результата не выполнится и цикл будет завершен. Текст программы может выглядеть следующим образом:

 

Метка Операция Операнд Комментарии
  MOV A,C ;переслать содержимое регистра C в аккумулятор.
  MVI B,00h ;обнулить регистр B, как это требуется в задании.
META: INR B ;увеличить на единицу содержимое регистра B, как это требуется в задании.
  CMP B ;содержимое регистра B сравнить с аккумулятором (с занесенным в него содержимым регистра C).
  JNZ META ;перейти к команде с адресом META, если содержимое регистра B не равно содержимому аккумулятора, цикл не завершен.
  --- --- ;в противном случае содержимое регистров B и C сравнялись и будет выполняться дальнейшая программа.

 

Пример 4. Обнулить 50 ячеек памяти в сторону уменьшения их адресов, начиная с ячейки с адресом 800016.

Решение. Как и в предыдущем примере, очевидно, что в данном случае нет необходимости обнулять все 50 ячеек памяти, напрямую адресуясь к ним пятидесятью командами прямой адресации. Удобнее всего организовать цикл из пятидесяти шагов, записывая в каждом шаге цикла в ячейку памяти, как во внешний регистр M, ноль, косвенно адресуясь к ней через содержимое указательной регистровой пары HL. Предварительно в HL заносится адрес 800016 первой ячейки и далее содержимое HL уменьшается на единицу, формируя, таким образом, адрес текущей обнуляемой ячейки в текущем шаге цикла. Для того, чтобы проконтролировать количество шагов цикла, необходимо организовать счетчик. В качестве счетчика можно использовать любой свободный регистр общего назначения, начальное содержимое которого будет равно количеству шагов цикла – 50. Далее на каждом шаге цикла содержимое этого регистра будет дикрементироваться на единицу. Цикл будет повторяться до тех пор, пока содержимое счетчика шагов цикла не станет равным нулю. В качестве условия перехода в начало тела цикла можно использовать, таким образом, ненулевой результат дикремента счетчика шагов цикла. Фрагмент программы может иметь следующий вид.

 

Метка Операция Операнд Комментарии
  LXI HL,8000h ;задать адрес 800016 первой обнуляемой ячейки памяти в регистровую пару HL.
  MVI B,50 ;задать начальное значение 5010 счетчику шагов цикла (в регистр B).
META: MOV M,0 ;обнулить ячейку памяти с текущим адресом, соответствующим содержимому ячейки памяти HL.
  DCX HL ;уменьшить на единицу содержимое регистровой пары HL (перейти к ячейки памяти со следующим адресом).
  DCR B ;уменьшить на единицу счетчик шагов цикла.
  JNZ META ;перейти к команде с адресом META, если результат предыдущей команды не равен нулю (не все 50 ячеек обнулены).
  --- --- ;в противном случае все 50 ячеек были обнулены и выполняется дальнейшая программа.

 

Задача 5. Написать программу, постоянно опрашивающую порт ввода 1, на четыре младших разряда которого поступает двоично-десятичный код, и выводящую соответствующую этому коду десятичную цифру на семисегментый индикатор, подключенный к порту вывода 2.

Решение. Прежде чем составлять программу, необходимо определиться: к каким разрядам порта вывода 2 будут подключены соответствующие сегменты семисегментного индикатора. Семисегментный индикатор имеет обозначение своих сегментов и выбранное подключение к разрядам порта вывода, как это показано на рис. 3.3.

 

 

Рис.3.3. Подключение семисегментного индикатора к разрядам порта вывода.

 

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

Воспользуемся следующим приемом: поместим в десяти последовательно расположенных вслед за программой ячейках памяти семисегментные коды в том порядке, в каком они приведены в табл.3.3. Адресу первой из этих ячеек присвоим метку TABL. Таким образом, в ячейке памяти с адресом TABL будет храниться семисегментный код цифры 0, в ячейке памяти с адресом TABL+1 – семисегментный код цифры 1 и т.д.

Поместим адрес, соответствующий метке TABL, в регистровую пару HL. Если теперь к содержимому регистровой пары HL прибавить двоично-десятичный код индицируемой цифры, то, очевидно, что в результате образуется адрес той ячейки памяти, в которой хранится семисегментный код индицируемой цифры. Если вывести теперь код из этой ячейки памяти, используя косвенную регистровую адресацию, в порт вывода 2, будет обеспечено свечение десятичной цифры, соответствующей входному двоичному коду. Ниже приведена программа, выполняющая поставленную задачу.

 

Метка Операция Операнд Комментарии
START: IN 01h ;ввести байт данных из порта ввода 116.
  ANI 00001111b ;обнулить не интересующие нас четыре старших разряда введенного байта.
  LXI DE,0000h ;обнулить регистровую пару DE.
  MOV E,A ;переслать двоичный код индицируемой цифры в младший регистр E регистровой пары DE.
  LXI HL,TABL ;записать в регистровую пару HL адрес, соответствующий метке TABL.
  DAD DE ;сложить содержимое регистровых пар DE и HL, результат занести в HL, т.е. в HL сформировался адрес ячейки памяти с индицируемым семисегментным кодом.
  MOV A,M ;переслать семисегментный код из адресованной ячейки памяти в аккумулятор.
  OUT 02h Вывести семисегменый код в порт вывода 216.
  JMP START ;перейти в начало программы.
TABL: DB 3Fh ;занести в последо- ;вательные ячейки па- ;мяти таблицу семи- ;сегментных кодов, на- ;чиная с ячейки с адре- ;сом, соответствую- ;щим метке TABL*.  
  DB 06h
  DB 5Bh
  DB 4Fh
  DB 66h
  DB 6Dh
  DB 7Dh
  DB 07h
  DB 7Fh
  DB 6Fh

 

*Примечание. Команда DB относится к так называемым директивам (или псевдокомандам) языка ассемблера. Подобные команды не имеют машинных кодов, а служат для указания уточнений программе транслятору - ассемблеру. В данном случае директива DB размещает в последовательных ячейках памяти, начиная с ячейки, имеющей адрес, соответствующий метке TABL, последовательность семисегменных кодов. При трансляции исходной программы в объектную ассемблер заменит директиву DB последовательностью необходимых машинных команд, выполняющих эту операцию.

 

Таблица 3.3. Соответствие десятичных цифр, двоично-десятичных и семисегментных кодов.

Десятичная цифра Двоичный (двоично-десятичный) код Семисегментный код
3F16
0616
5B16
4F16
6616
6D16
7D16
0716
7F16
6F16

 

Пример 6. Из чисел, хранимых в 120 последовательных ячейках рамяти, начиная с ячейки с адресом 01A616, подсчитать количество тех чисел A, удовлетворяющих условию: 10£A³50. Результат подсчета вывести в порт вывода 7.

Решение. Очевидно, что для решения данной задачи требуется реализовать операции сравнения и в зависимости от результата сравнения выполнить условный переход в нужное место программы. Т.к. число A может иметь 120 значений, то необходимо организовать цикл из 120 шагов. При решении задачи сначала проверяется принадлежность числа A верхней границе интервала, а затем нижней в каждом шаге цикла. И только если число A попадает в заданный интервал, то факт такого попадания подсчитывается в счетчике. В противном случае значение счетчика в данном шаге цикла остается неизменным. Результат подсчета и есть количество чисел A, удовлетворяющих требуемому нестрогому неравенству. Текст программы приводится ниже.

 

Метка Операция Операнд Комментарии
  LXI HL,01A6h ;поместить в регистровую пару HL адрес первой из 120 ячеек памяти.
  MVI B,00h ;задать начальное нулевое значение в регистр B, использующийся в качестве счетчика количества чисел A, попавших в заданный интервал.
  MVI C,120 ;задать начальное значение в регистр C, использующийся для подсчета количества шагов цикла, в котором будут проверяться 120 ячеек памяти.
META: MVI A,50 ;поместить в аккумулятор верхнюю границу интервала – значение 50.
  CMP M ;сравнить текущее значение числа A (содержимое ячейки памяти по текущему адресу) с верхней границей (числом 50 в аккумуляторе).
  JC MK2 ;перейти к команде с меткой MK2, если проверяемое число больше 50 (в результате сравнения в предыдущей команде получилась отрицательное значение, т.е. был заем единицы из девятого в восьмой разряд при вычитании).
  MOV A,M ;поместить текущее значение проверяемого числа из памяти в аккумулятор.
  CPI ;сравнить текущее значение числа с нижней границей (числом 50).
  JC MK2 ;перейти к команде с меткой MK2, если проверяемое число меньше 10 (в результате сравнения в предыдущей команде получилась отрицательное значение).
  INR B ;содержимое регистра B (счетчика чисел A) увеличить на единицу, если проверяемое число оказалось £50 и ³10
MR2: INX HL ;перейти к адресу следующей ячейки памяти, проинкрементировав содержимое указательной регистровой пары HL.
  DCR C ;уменьшить на единицу содержимое регистра C (счетчика проверенных ячеек памяти).
  JNZ META ;перейти к команде с меткой META (следующему шагу цикла проверки следующего значения числа A), если не все 120 ячеек проверены (содержимое регистра C не обнулилось).
  MOV A,B ;переслать результат подсчета количества чисел 10£A³50 в аккумулятор.
  OUT 07h ;результат подсчета вывести в порт вывода 716.
  HLT   ;остановить счетчик команд и дождаться внешнего аппаратного прерывания.