УСЛОВНЫЕ КОМАНДЫ
Условная команда — это такая команда, выполнение которой зависит от каких-либо условий. Например, требуется определенная установка флажков или необходимо наличие нуля в некотором регистре. Самыми употребительными из всех условных команд являются команды условной передачи управления.
Команды JE, JNE, JA, JB, . (и т.д.) — условная передача управления (переход, если условие удовлетворяется).
Команды SETE, SETNE, SETA, SETB, . (и т.д.) — установка байта по условию.
Команды LOOP, LOOPE/LOOPZ, LOOPNE/LOOPNZ — переход для повторения цикла.
Команды JCXZ, JECXZ — переход, если значение счетчика равно нулю.
Команды условной передачи управления
Если условие истинно (TRUE), то происходит переход по указанному адресу, иначе (FALSE) переход не происходит и выполняется следующая по порядку команда.
Для перехода задается относительный адрес, то есть, смещение относительно адреса команды, следующей после данной команды. Задаваемый на ассемблере адрес может иметь тип SHORT или тип NEAR. Адрес типа SHORT кодируется в команде всего одним байтом. Поэтому для адреса типа SHORT переход может быть только в пределах . Адрес типа NEAR кодируется двумя байтами в режиме 16 бит и четырьмя байтами в режиме 32 бита.
Всего есть 16 разных условий, им соответствует 16 разных кодов команд на уровне процессора. Но на уровне языка ассемблера эти условия превращаются в 30 разных команд. Таким образом, одна и та же команда процессора может иметь разные имена (синонимы) на языке ассемблера.
Кодирование
| Команда (asm) | Тип адреса | Код | Формат |
| J(cond) | SHORT | 70 +cond | —-cond addr(1) |
| J(cond) | NEAR | 0F 80 +cond | —-cond addr(2/4) |
Примечание. Код условия (cond) может иметь значение от 0 до F (hex).
Условия при сравнении чисел без знака
| Код (cond) | Команда (asm) | Флажки | Условие (eng) | Условие (рус) |
| 0100 | JE | ZF=1 | if equal | если равно |
| 0101 | JNE | ZF=0 | if not equal | если не равно |
| 0010 | JB /JNAE | CF=1 | if below /not above or equal | если меньше /не больше или равно |
| 0011 | JAE /JNB | CF=0 | if above or equal /not below | если больше или равно /не меньше |
| 0110 | JBE /JNA | CF=1 or ZF=1 | if below or equal /not above | если меньше или равно /не больше |
| 0111 | JA /JNBE | CF=0 and ZF=0 | if above /not below or equal | если больше /не меньше или равно |
Условия при сравнении чисел со знаком
| Код (cond) | Команда (asm) | Флажки | Условие (eng) | Условие (рус) |
| 0100 | JE | ZF=1 | if equal | если равно |
| 0101 | JNE | ZF=0 | if not equal | если не равно |
| 1100 | JL /JNGE | SF!=OF | if less /not greater or equal | если меньше /не больше или равно |
| 1101 | JGE /JNL | SF=OF | if greater or equal /not less | если больше или равно /не меньше |
| 1110 | JLE /JNG | ZF=1 or SF!=OF | if less or equal /not greater | если меньше или равно /не больше |
| 1111 | JG /JNLE | ZF=0 or SF=OF | if greater /not less or equal | если больше /не меньше или равно |
Условия по отдельным флажкам
| Код (cond) | Команда (asm) | Флажки | Условие (eng) | Условие (рус) |
| 0100 | JZ | ZF=1 | if zero | если есть флаг нуля |
| 0101 | JNZ | ZF=0 | if not zero | если нет флага нуля |
| 1000 | JS | SF=1 | if sign | если есть флаг знака |
| 1001 | JNS | SF=0 | if not sign | если нет флага знака |
| 0010 | JC | CF=1 | if carry | если есть флаг переноса |
| 0011 | JNC | CF=0 | if not carry | если нет флага переноса |
| 0000 | JO | OF=1 | if overflow | если есть флаг переполнения |
| 0001 | JNO | OF=0 | if not overflow | если нет флага переполнения |
| 1010 | JP /JPE | PF=1 | if parity /parity even | если есть флаг четности /четный |
| 1011 | JNP /JPO | PF=0 | if not parity /parity odd | если нет флага четности /нечетный |
Все условия, упорядоченные по кодам (cond)
и соответствующие коды операций
В этой таблице показаны также и соответствующие коды операций для команд с адресом типа SHORT и для команд с адресом типа NEAR.
| Код для SHORT | Код для NEAR | Код (cond) | Команда (asm) | Флажки |
| 70 | 0F 80 | 0000 | JO | OF=1 |
| 71 | 0F 81 | 0001 | JNO | OF=0 |
| 72 | 0F 82 | 0010 | JB/JNAE/JC | CF=1 |
| 73 | 0F 83 | 0011 | JAE/JNB/JNC | CF=0 |
| 74 | 0F 84 | 0100 | JE/JZ | ZF=1 |
| 75 | 0F 85 | 0101 | JNE/JNZ | ZF=0 |
| 76 | 0F 86 | 0110 | JBE/JNA | CF=1 or ZF=1 |
| 77 | 0F 87 | 0111 | JA/JNBE | CF=0 and ZF=0 |
| 78 | 0F 88 | 1000 | JS | SF=1 |
| 79 | 0F 89 | 1001 | JNS | SF=0 |
| 7A | 0F 8A | 1010 | JP/JPE | PF=1 |
| 7B | 0F 8B | 1011 | JNP/JPO | PF=0 |
| 7C | 0F 8C | 1100 | JL/JNGE | SF!=OF |
| 7D | 0F 8D | 1101 | JGE/JNL | SF=OF |
| 7E | 0F 8E | 1110 | JLE/JNG | ZF=1 or SF!=OF |
| 7F | 0F 8F | 1111 | JG/JNLE | ZF=0 or SF=OF |
Команда на языке ассемблера
| Форматы | Примеры | ||||||
| J(cond) label |
|
Команды установки байта по условию
SETE, SETNE, SETA, SETB, .
В команде установки байта SET(cond) используется тот же самый набор из 16 условий, что и в команде условной передачи управления J(cond) . И в мнемонике на языке ассемблера эти условия представляются такими же 30-ю способами.
На языке ассемблера команда имеет один операнд. Это может быть либо 8-битный регистр, либо адрес в памяти.
Команда заносит в байт, указанный в операнде, значение 1, если условие выполняется (TRUE), и значение 0 в противном случае (FALSE).
Кодирование
| Команда (asm) | Код | Формат |
| SET(cond) | 0F 90 +cond /000 | —-cond NNN |
Примечания.
1. Код условия (cond) может иметь значение от 0 до F (hex).
2. В байте (mod,reg,r/m) трехбитное поле reg должно быть равно 000.
Команда на языке ассемблера
| Форматы | Примеры | ||||||
| SET(cond) reg8 |
| ||||||
| SET(cond) mem8 |
|
Команда перехода для повторения цикла
LOOP, LOOPE/LOOPZ, LOOPNE/LOOPNZ
Эти команды используются для управления циклом со счетчиком в регистре CX (или в регистре ECX ).
При выполнении команды сначала происходит декремент (уменьшение на единицу) счетчика в регистре. (При этом никакие флажки не изменяются.) В дальнейшем, при проверке условий, действует уже измененное значение счетчика.
Если атрибут размера адреса равен 16 битам, то счетчиком служит регистр CX , если этот атрибут равен 32 битам, то регистр ECX .
Если условия (см. в таблице ниже) выполняются, то происходит переход по указанному адресу.
В условиях, кроме значения счетчика, учитывается еще и флажок нуля. Значение флажков вырабатываются командами, предшествующими команде LOOP.
Для перехода задается адрес типа SHORT, то есть, относительный адрес из одного байта. Поэтому переход может быть в пределах от (-128) до (+127) относительно адреса команды, следующей после команды LOOP.
Кодирование
См. в таблице ниже.
Команда перехода по счетчику
Переход по указанному адресу происходит в том случае, если значение счетчика равно нулю. Счетчиком служит регистр CX , если установлен режим 16 бит, или регистр ECX , если режим 32 бита.
Для перехода задается адрес типа SHORT, то есть, относительный адрес из одного байта. Поэтому переход может быть в пределах от (-128) до (+127) относительно адреса команды, следующей после команды JCXZ.
Данная команда не изменяет содержимое регистра CX (или ECX ) и не изменяет флажки.
Обычно процессор выполняет команду JCXZ дольше, чем вот такие две команды: команда сравнения содержимого регистра с нулем плюс команда условного перехода по флажку. По этой причине команда JCXZ обычно заменяется на другие команды.
What do the assembly instructions 'seta' and 'setb' do after repz cmpsb?
I’m having trouble understanding what the following lines of assembly do:
I understand after debugging, the first instruction compares the bytes in registers rsi and rdi , byte by byte.
Then it sets the lower bytes of rdx and rax according based on that instruction.
My confusion is, when I looked up this instruction online, it said seta sets the lower byte to 0x01 if its above a certain value, otherwise its 0x00. Similar for setb , which sets the byte to 0x01 if its below a certain value.
My question is what value, and how is it related to the above instruction?
Базовая система команд микропроцессора
Базовую систему команд микропроцессора можно условно разделить на несколько групп по функциональному назначению:
Кроме базовой системы команд микропроцессора существуют также команды расширений:
- X87 – расширение, содержащее команды математического сопроцессора (работа с вещественными числами)
- MMX – расширение, содержащее команды для кодирования/декодирования потоковых аудио/видео данных;
- SSE – расширение включает в себя набор инструкций, который производит операции со скалярными и упакованными типами данных;
- SSE2 – модификация SSE, содержит инструкции для потоковой обработки целочисленных данных, что делает это расширение более предпочтительным для целочисленных вычислений, нежели использование набора инструкций MMX, появившегося гораздо раньше;
- SSE3, SSE4 – содержат дополнительные инструкции расширения SSE.
В таблице команд приняты следующие обозначения:
r – регистр
m – ячейка памяти
c – константа
8, 16, 32 – размер в битах
На все базовые команды процессора накладываются следующие ограничения:
- Нельзя в одной команде оперировать двумя областями памяти одновременно. Если такая необходимость возникает, то нужно использовать в качестве промежуточного буфера любой доступный в данный момент регистр общего назначения.
- Нельзя оперировать сегментным регистром и значением непосредственно из памяти. Поэтому для выполнения такой операции нужно использовать промежуточный объект. Это может быть регистр общего назначения или стек.
- Нельзя оперировать двумя сегментными регистрами. Это объясняется тем, что в системе команд нет соответствующего кода операции. Но необходимость в таком действии часто возникает. Выполнить такую пересылку можно, используя в качестве промежуточных регистры общего назначения. Например,
Команды передачи данных
Основной командой передачи данных является команда MOV , осуществляющая операцию присваивания:
Команда MOV присваивает значению операнда приемника значение операнда источника. В качестве приемника могут выступать регистр общего назначения, сегментный регистр или ячейка памяти, в качестве источника могут выступать константа, регистр общего назначения, сегментный регистр или ячейка памяти. Оба операнда должны быть одного размера.
Команды передачи данных представлены в таблице.
| Команда | Операнды | Пояснение | Описание |
| MOV | r(m)8,r8 r(m)16,r16 r(m)32,r32 r8,r(m)8 r16,r(m)16 r32,r(m)32 r(m)8,c8 r(m)16,c16 r(m)32,c32 |
r(m)8=r8 r(m)16=r16 r(m)32=r32 r8=r(m)8 r16=r(m)16 r32=r(m)32 r(m)8=с8 r(m)16=с16 r(m)32=с32 |
Пересылка операндов |
| XCHG | r(m)8, r8 r8, r(m)8 r(m)16,r16 r16, r(m)16 r(m)32, r32 r32, r(m)32 |
r(m)8 ↔r8 r8 ↔r(m)8 r(m)16↔r16 r16 ↔r(m)16 r(m)32↔r32 r32 ↔r(m)32 |
Обмен операндов |
| BSWAP | r32 | TEMP ← r32 r32[7..0]←TEMP[31..24] r32[15..8]←TEMP[23..16] r32[23..16]←TEMP[15..8] r32[31..24]←TEMP[7..0] |
Перестановка байтов из порядка «младший – старший» в порядок «старший – младший» |
| MOVSX | r16, r(m)8 r32, r(m)8 r32, r(m)16 |
r16,r(m)8 DW ← DB r32,r(m)8 DD ← DB r32,r(m)16 DD ← DW |
Пересылка с расширением формата и дублированием знакового бита |
| MOVZX | r16,r(m)8 r32,r/m8 r32,r/m16 |
r16,r(m)8 DW ← DB r32,r(m)8 DD ← DB r32,r(m)16 DD ← DW |
Пересылка с расширением формата и дублированием нулевого бита |
| XLATXLATB | m8 | AL=DS:[(E)BX+unsigned AL] | Загрузить в AL байт из таблицы в сегменте данных, на начало которой указывает EBX (ВХ); начальное значение AL играет роль смещения |
| LEA | r16, m r32, m |
r16=offset m r32=offset m |
Загрузка эффективного адреса |
| LDS | r16,m16 r32,m16 |
DS:r=offset m | Загрузить пару регистров из памяти |
| LSS | SS:r=offset m | ||
| LES | ES:r=offset m | ||
| LFS | FS:r=offset m | ||
| LGS | GS:r=offset m |
Команды установки единичного бита
Проверяют условие состояния битов регистра EFLAGS и, если условие выполняется, то младший бит операнда устанавливается в 1, в противном случае в 0. Анализ битов производится аналогично условным переходам.
| Команда | Операнды | Пояснение |
| SETA SETNBE |
r(m)8 | CF=0 и ZF=0 |
| SETAE SETNB SETNC |
CF=0 | |
| SETB SETC SETNAE |
CF=1 | |
| SETBE SETNA |
CF=1 или ZF=1 | |
| SETE SETZ |
ZF=1 | |
| SETG SETNLE |
ZF=0 и SF=OF | |
| SETGE SETNL |
SF=OF | |
| SETL SETNGE |
SF!=OF | |
| SETLE SETNG |
SF!=OF или ZF=1 | |
| SETNE SETNZ |
ZF=0 | |
| SETNO | OF=0 | |
| SETNP SETPO |
PF=0 | |
| SETNS | SF=0 | |
| SETO | OF=1 | |
| SETP SETPE |
PF=1 | |
| SETS | SF=1 |
Команды работы со стеком
| Команда | Операнды | Пояснение | Описание |
| PUSH | r(m)32 r(m)16 c32 |
ESP=ESP-4; SS:ESP=r(m)32/c SP=SP-2; SS:SP=r(m)16 |
Поместить операнд в вершину стека |
| POP | r(m)32 r(m)16 |
r(m)32=SS:ESP; ESP=ESP+4 r(m)16=SS:SP; SP=SP+2; |
Извлечь операнд из вершины стека |
| PUSHA PUSHAD |
r(m)32 r(m)16 |
— | Поместить в стек регистры EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP |
| POPA POPAD |
— | Извлечь из стека содержимое и заполнить регистры EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP | |
| PUSHF | — | Поместить в вершину стека регистр EFLAGS | |
| POPF | — | Извлечь содержимое вершины стека и заполнить регистр EFLAGS |
Команды ввода-вывода
Микропроцессор может передавать данные в порты ввода-вывода, которые поддерживаются аппаратно и используют соответствующие своим предназначениям линии ввода-вывода процессора. Аппаратное адресное пространство ввода-вывода процессора не является физическим адресным пространством памяти. Адресное пространство ввода-вывода состоит из 64Кбайт индивидуально адресуемых 8-битных портов ввода-вывода, имеющих адреса 0…FFFFh. Адреса 0F8h…0FFh являются резервными. Любые два последовательных 8-битных порта могут быть объединены в 16-битный порт, 4 последовательных 8-битных порта – в 32-битный порт.
| Команда | Операнды | Пояснение | Описание |
| IN | AL,c8 AX,c8 EAX,c8 AL,DX AX,DX EAX,DX |
AL= port byte AX= port word EAX= port dword AL= [DX-port] AX= [DX-port] EAX= [DX-port] |
Ввод из порта |
| OUT | c8, AL c8, AX c8, EAX DX, AL DX, AX DX, EAX |
port byte=AL port word=AX port dword=EAX [DX-port]=AL [DX-port]=AX [DX-port]=EAX |
Вывод в порт |
| INSB INSW INSD |
— | ES:(E)DI = [DX-port] | Вводит данные из порта, адресуемого DX в ячейку памяти ES:[(E)DI]. После ввода 1, 2 или 4-байтного слова данных EDI/DI корректируется на 1,2,4. При наличии префикса REP процесс продолжается, пока EСХ>0 |
| OUTSB OUTSW OUTSD |
— | [DX-port]=DS:(E)SI | Выводит данные из ячейки памяти, определяемой регистрами DS:[(E)SI], в порт, адрес которого находится в DX. После вывода данных производится коррекция указателя ESI/SI на 1,2 или 4 |
Команды целочисленной арифметики
| Команда | Операнды | Пояснение | Описание |
| ADD | r(m)8,с8 r(m)16,с16 r(m)32,с32 r(m)8,r8 r(m)16,r16 r(m)32,r32 r8,r(m)8 r16,r(m)16 r32,r(m)32 |
r(m)8=r(m)8+с8 r(m)16=r(m)16+с16 r(m)32=r(m)32+c32 r(m)8=r(m)8+r8 r(m)16=r(m)16+r16 r(m)32=r(m)32+r32 r8=r8+r(m)8 r16=r16+r(m)16 r32=r32+r(m)32 |
Сложение целых чисел |
| ADC | Сложение целых чисел с учетом флага переноса CF | ||
| INC | r(m)8 r(m)16 r(m)32 |
r/m8=r/m8±1 r(m)16=r(m)16±1 r(m)32=r(m)32±1 |
Увеличение на 1 |
| DEC | Уменьшение на 1 | ||
| SUB | r(m)8,с8 r(m)16,с16 r(m)32,с32 r(m)8,r8 r(m)16,r16 r(m)32,r32 r8,r(m)8 r16,r(m)16 r32,r(m)32 |
r(m)8=r(m)8-с8 r(m)16=r(m)16-с16 r(m)32=r(m)32-c32 r(m)8=r(m)8-r8 r(m)16=r(m)16-r16 r(m)32=r(m)32-r32 r8=r8-r(m)8 r16=r16-r(m)16 r32=r32-r(m)32 |
Вычитание целых чисел |
| SBB | Вычитание с учетом флага переноса CF | ||
| CMP | r(m)8,с8 r(m)16,с16 r(m)32,с32 r(m)8,r8 r(m)16,r16 r(m)32,r32 r8,r(m)8 r16,r(m)16 r32,r(m)32 |
r(m)8-с8 r(m)16-с16 r(m)32-c32 r(m)8-r8 r(m)16-r16 r(m)32-r32 r8-r(m)8 r16-r(m)16 r32-r(m)32 |
Сравнение целых чисел По результату сравнения устанавливаются флаги CF PF AF ZF SF OF |
| NEG | r(m)8 r(m)16 r(m)32 |
r(m)8=-r(m)8 r(m)16=-r(m)16 r(m)32=-r(m)32 |
Изменение знака числа |
| MUL | r(m)8 r(m)16 r(m)32 |
AX=AL*r(m)8 DX:AX=AX*r(m)16 EDX:EAX=EAX*r(m)32 |
Умножение без знака |
| IMUL | r(m)8 r(m)16 r(m)32 r16,r(m)16 r32,r(m)32 r16,r(m)16,c r32,r(m)32,c r16,c r32,c |
AX=AL*r(m)8 DX:AX=AX*r(m)16 EDX:EAX=EAX*r(m)32 r16=r16*r(m)16 r32=r32*r(m)32 r16=r(m)16*c16 r32=r(m)32*c32 r16=r16*c16 r32=r32*c32 |
Умножение со знаком |
| DIV | r(m)8 r(m)16 r(m)32 |
AL=AX/r(m)8, AH=mod AX=DX:AX/r(m)16, DX=mod EAX=EDX:EAX/r(m)32, EDX=mod |
Деление без знака |
| IDIV | Деление со знаком |
Особого внимания среди рассмотренных команд целочисленной арифметики заслуживает команда CMP , которая вычитает второй операнд из первого и не сохраняет результат, а устанавливает биты OF, SF, ZF, AF, PF, CF регистра признаков EFLAGS в соответствии с результатом. Команда CMP чаще всего предшествует командам знакового или беззнакового условных переходов.
Логические команды
Выполнение логических операций описано здесь
| Команда | Операнды | Пояснение | Описание |
| AND | r(m)8,с8 r(m)16,с16 r(m)32,с32 r(m)8,r8 r(m)16,r16 r(m)32,r32 r8,r(m)8 r16,r(m)16 r32,r(m)32 |
r(m)8=r(m)8 Ф с8 r(m)16=r(m)16 Ф с16 r(m)32=r(m)32 Ф c32 r(m)8=r(m)8 Ф r8 r(m)16=r(m)16 Ф r16 r(m)32=r(m)32 Ф r32 r8=r8 Ф r(m)8 r16=r16 Ф r(m)16 r32=r32 Ф r(m)32 |
Логическое умножение (И), конъюнкция |
| OR | Логическое сложение (ИЛИ), дизъюнкция | ||
| XOR | Исключающее ИЛИ | ||
| NOT | r(m)8 r(m)16 r(m)32 |
r(m)8= |
Сдвиговые команды
Выполнение сдвиговых операций в языке Си рассмотрено здесь .
| Команда | Операнды | Пояснение | Описание |
| SHR | r(m)8 r(m)8,CL r(m)8,с r(m)16 r(m)16,CL r(m)16,c r(m)32 r(m)32,CL r(m)32,c |
r(m)8 на 1 раздяд r(m)8 на CL разрядов r(m)8 на с разрядов r(m)16 на 1 разряд r(m)16 на CL разрядов r(m)16 на c разрядов r(m)32 на 1 разряд r(m)32 на CL разрядов r(m)32 на c разрядов |
Логический сдвиг вправо |
| SAR | Арифметический сдвиг вправо (старшие разряды заполняются значением знакового) | ||
| SHL SAL |
Логический (арифметический) сдвиг влево | ||
| ROR | Циклический сдвиг вправо | ||
| ROL | Циклический сдвиг влево | ||
| RСR | Циклический сдвиг вправо через перенос | ||
| RCL | Циклический сдвиг влево через перенос |
Команды циклического сдвига выполняются в соответствии со схемой
Команды коррекции двично-десятичных чисел
Команды коррекции двоично-десятичных чисел не имеют операндов и используют операнд по умолчанию, хранящийся в регистре AX (паре регистров AH:AL ).
Команды преобразования типов
Команды преобразования типов предназначены для корректного изменения размера операнда, заданного неявно в регистре-аккумуляторе ( EAX , AX , AL ). Непосредственно после аббревиатуры команды операнд не указывается.
| Команда | Пояснение | Описание |
| CBW | AX=(DW)AL | 2 байта ← 1 байт |
| CWDE | EAX=(DD)AX | 4 байта ← 2 байта |
| CWD | DX:AX=(DD)AX | 4 байта ← 2 байта |
| CDQ | EDX:EAX=(DQ)EAX | 8 байт ← 4 байта |
Команды управления флагами
Команды управления флагами предназначены для сброса или установки соответствующего бита регистра признаков EFLAGS . Команды управления флагами не имеют операндов.
| Команда | Пояснение | Описание |
| CLC | CF = 0 | Сброс бита переноса |
| CLD | DF=0 | Сброс бита направления |
| CMC | CF=!CF | Инверсия бита переноса |
| STC | CF=1 | Установка бита переноса |
| STD | DF=1 | Установка бита направления |
| STI | IF=1 | Установка бита прерывания |
Команды прерываний
Команды прерываний предназначены для управления программными прерываниями.
Прерывание – это, как правило, асинхронная остановка работы процессора, вызванная началом работы устройства ввода-вывода. Исключением являются синхронные прерывания, возникающие при определении некоторых предопределенных условий в процессе выполнения команды.
Когда поступает сигнал о прерывании, процессор останавливает выполнение текущей программы и переключается на выполнение обработчика прерывания, заранее записанного для каждого прерывания.
Архитектура IA-32 поддерживает 17 векторов аппаратных прерываний и 224 пользовательских.
Команда INT вызывает обработчик указанного операндом прерывания (константой). Операнд определяет номер вектора системного прерывания BIOS от 0 до 255, представленный в виде беззнакового 8-битного целого числа. При вызове обработчика прерывания в стеке сохраняются регистры EIP, CS и EFLAGS.
Прерывание по переполнению вызывается отдельной командой INTO и имеет вектор 04h.
| Команда | Пояснение | Описание |
| INT с | EIP → стек CS → стек EFLAGS → стек переход к вектору c |
Программное прерывание |
| INTO | OF=1 | Прерывание по переполнению |
| IRET | EFLAGS ← стек CS ← стек EIP ← стек возврат |
Возврат из обработчика прерывания |
Команды передачи управления
| Команда | Операнды | Пояснение | Описание |
| JMP | метка r(m)16 r(m)32 |
метка адрес в r(m)16 адрес в r(m)32 |
Безусловный переход на адрес, указанный операндом |
Команды обращения к процедуре (функции)
| Команда | Операнды | Пояснение | Описание |
| CALL | метка r(m)16 r(m)32 |
метка адрес в r(m)16 адрес в r(m)32 |
Вызов процедуры, указанной операндом |
| RET | — c16 |
— Удаляет из стека c16 байт |
Возврат из процедуры |
Команды поддержки языков высокого уровня
| Команда | Операнды | Пояснение | Описание |
| ENTER | c16, c8 | PUSH EBP MOV EBP, ESP |
Подготовка стека при входе в процедуру. Константа с16 указывает количество байт, резервируемых в стеке для локальных идентификаторов, константа с8 определяет вложенность процедуры |
| LEAVE | — | POP EBP | Приведение стека в исходное состояние |
| BOUND | r16, m16&16 r32, m32&32 |
m16<r16<m16&16 m32<r16<m32&32 |
Проверка индекса массива: сравнивает значение в регистре, заданном первым операндом с двумя значениями, расположенными последовательно в ячейке памяти, адресуемой вторым операндом. |
Команды организации циклов — используют регистр ECX по умолчанию в качестве счетчика числа повторений цикла. Каждый раз при выполнении команды LOOPсс значение регистра ECX уменьшается на 1, а затем сравнивается с 0. Если ECX =0 , выполнение цикла заканчивается, и продолжает выполняться код программы, записанный после команды LOOPcc . Если ECX содержит ненулевое значение, то осуществляется переход по адресу операнда команды LOOPcc .
Команды условных переходов — проверяют состояние одного или нескольких битов регистра признаков и при выполнении условия осуществляют передачу программного управления в другую точку кода, задаваемую операндом. Указанный класс команд не запоминает информацию для возврата. Операнд определяет адрес команды, которой должно быть передано управление.
Команды логических операций микроконтроллера 8051
В этой группе 25 команд, их краткое описание приведено в таблице. Эти команды позволяют выполнять операции над байтами: логическое И(/\), логическое ИЛИ(\/), исключающее ИЛИ ( Å ), инверсию (NOT), сброс в нулевое значение и сдвиг.
Логическое И аккумулятора и регистра
Логическое И аккумулятора и прямоадресуемого
Логическое И аккумулятора и байта из внутренней
Логическое И аккумулятора и непосредственного
Логическое И прямоадресуемого байта и
Логическое И прямоадресуемого байта и
Логическое ИЛИ аккумулятора и регистра
Логическое ИЛИ аккумулятора и прямоадресуемого
Логическое ИЛИ аккумулятора и байта из внутренней
Логическое ИЛИ аккумулятора и константы
Логическое ИЛИ прямоадресуемого байта и
Логическое ИЛИ прямоадресуемого байта и константы
Исключающее ИЛИ аккумулятора и регистра
Исключающее ИЛИ аккумулятора и прямоадресуемого
Исключающее ИЛИ аккумулятора и байта из
внутренней памяти данных
Исключающее ИЛИ аккумулятора и
Исключающее ИЛИ прямоадресуемого байта и
Исключающее ИЛИ прямоадресуемого байта и
Сдвиг аккумулятора влево циклический
(A n+1 ) ← (A n ), n=0÷6,
Сдвиг аккумулятора влево через перенос
(A n+1 ) ← (A n ), n=0÷6
Сдвиг аккумулятора вправо циклический
(A n ) ← (A n+1 ), n=0÷6,
Сдвиг аккумулятора вправо через перенос
(A n ) ← (A n+1 ), n=0÷6
Обмен местами тетрад в аккумуляторе
Команды операций над битами микроконтроллера 8051
Группа состоит из 12 команд, краткое описание которых приведено в таблице. Эти команды позволяют выполнять операции над отдельными битами: сброс, установку, инверсию бита, а также логические И(/\) и ИЛИ(\/). В качестве «логического» аккумулятора, участвующего во всех операциях с двумя операндами, выступает признак переноса С, в качестве операндов могут использоваться 128 бит из бит-адресуемой области внутренней памяти данных и регистры специальных функций, допускающие адресацию отдельных бит.
Операции с битами
Логическое И бита и переноса
Логическое И инверсии бита и переноса
Логическое ИЛИ бита и переноса
Логическое ИЛИ инверсии бита и
Пересылка бита в перенос
Пересылка переноса в бит
Команды передачи управления микроконтроллера 8051
Группа представлена командами безусловного и условного переходов, командами вызова подпрограмм и командами возврата из подпрограмм.
Команды передачи управления
Длинный переход в полном
объеме памяти программ
Абсолютный переход внутри
A 10 a 9 a 8 0000
страницы в 2 Кб
затем (PC 0-10 ) ← ad11
переход внутри страницы в
затем (PC) ← (PC)+rel
Переход, если аккумулятор
если (A)=0, то (PC) ← (PC)+rel
Переход, если аккумулятор не
если (A)≠0, то (PC) ← (PC)+rel
Переход, если перенос равен
Переход, если перенос равен
Переход, если бит равен
если (b)=l, то (PC) (PC)+rel
Переход, если бит равен нулю
если (b)=0, то (PC) ← (PC)+rel
Переход, если бит установлен,
(PC) ← (PC)+3, затем если (b)=1,
с последующим сбросом бита
Декремент регистра и
переход, если не нуль
(PC) ← (PC)+2, (ad) ← (ad) -1,
байта и переход, если не нуль
затем если (ad) ≠ 0, то
Сравнение аккумулятора с
(PC) ← (PC)+3, если (A)≠(ad), то
прямоадресуемым байтом и
(PC) ← (PC)+rel, при этом
переход, если не равно
Сравнение аккумулятора с
(PC) ← (PC)+3, если (A)≠ #d, то
константой и переход, если не
(PC) ← (PC)+rel, при этом
Сравнение регистра с
(PC) ← (PC)+3, если (Rn)≠ #d, то
константой и переход, если не
(PC) ← (PC)+rel, при этом
Сравнение байта во
(PC) ← (PC)+3, если ((Ri))≠ #d,
внутренней памяти данных с
то (PC) ← (PC)+rel, при этом
константой и переход, если не
если ((Ri))< #d, то (C) 1,
Длинный вызов подпрограммы
a 10 a 9 a 8 10001
(PC) ← (PC) + 2, (SP) ← (SP)+1,
подпрограммы в пределах
страницы в 2 Кб
затем (PC 0-10 ) ← ad11
Возврат из подпрограммы
(PC 8…15 ) ← ((SP)), (SP) ← (SP)-1,
затем (PC 0…7 ) ← ((SP)),
Возврат из подпрограммы
(PC 8…15 ) ← ((SP)), (SP) ← (SP)-1,
затем (PC 0…7 ) ← ((SP)),
Команда безусловного перехода LJMP
(L – Long – длинный)
осуществляет переход по абсолютному 16-битному адресу, указанному в теле команды, т. е. команда обеспечивает переход в любую точку памяти программ.
Действие команды AJMP (А – Absolute – абсолютный) аналогично команде LJMP , однако в теле команды указаны лишь 11 младших разрядов
адреса. Поэтому переход осуществляется в пределах страницы размером 2 Кбайт, при этом надо иметь в виду, что сначала содержимое счетчика команд увеличивается на 2 и только потом заменяются 11 разрядов адреса.
В отличие от предыдущих команд, в команде SJMP (S – Short –
короткий) указан не абсолютный, а относительный адрес перехода. Величина смещения rel рассматривается как число со знаком, а, следовательно, переход возможен в пределах – 128. +127 байт относительно адреса команды, следующей за командой SJMP .
Команда косвенного перехода JMP @A+DPTR позволяет вычислять
адрес перехода в процессе выполнения самой программы.
Командами условного перехода можно проверять следующие условия: — JZ — аккумулятор содержит нулевое значение;
— JNZ — аккумулятор содержит не нулевое значение — JC — бит переноса С установлен;
— JNC — бит переноса С не установлен;
— JB — прямо адресуемый бит равен 1
— JNB — прямо адресуемый бит равен 0;
— JBC — прямо адресуемый бит равен 1 и сбрасывается в нулевое значение при выполнении команды.
Все команды условного перехода рассматриваемых микро-ЭВМ содержат короткий относительный адрес, т. е. переход может осуществляться в пределах –128. +127 байт относительно следующей команды.
Команда DJNZ предназначена для организации программных циклов.
Регистр Rn или байт по адресу ad, указанные в теле команды, содержат счетчик повторений цикла, а смещение rеl — относительный адрес перехода к началу цикла. При выполнении команды содержимое счетчика уменьшается на 1 и проверяется на 0. Если значение содержимого счетчика не равно 0, то осуществляется переход на начало цикла, в противном случае выполняется следующая команда.
Команда CJN удобна для реализации процедур ожидания внешних
событий. В теле команды указаны «координаты» двух байт и относительный адрес перехода rel. В качестве двух байт могут быть использованы, например, значения содержимого аккумулятора и прямо адресуемого байта или косвенно адресуемого байта и константы. При выполнении команды значения указанных двух байт сравниваются и в случае, если они не одинаковы, осуществляется
переход. Например, команда
WAIT: CJNE A, P0, WAIT
будет выполняться до тех пор, пока значения на линиях порта P0 не совпадут со значениями содержимого аккумулятора.
Действие команд вызова процедур полностью аналогично действию команд безусловного перехода. Единственное отличие состоит в том, что они сохраняют в стеке адрес возврата.
Команда возврата из подпрограммы RET восстанавливает из стека
значение содержимого счетчика команд, а команда возврата из процедуры обработки прерывания RETI , кроме того, разрешает прерывание обслуженного
уровня. Команды RET и RETI не различают, какой командой – LCALL или ACALL – была вызвана подпрограмма, так как и в том, и в другом случае в стеке
сохраняется полный 16-разрядный адрес возврата.
В заключение следует отметить, что большинство ассемблеров допускают обобщенную мнемонику JMP – для команд безусловного перехода и
CALL – для команд вызова подпрограмм. Конкретный тип команды определяется ассемблером, исходя из «длины» перехода или вызова.
Лабораторная работа № 1
ИЗУЧЕНИЕ СИСТЕМЫ КОМАНД И ПРИЕМОВ ПРОГРАММИРОВАНИЯ ОДНОКРИСТАЛЬНЫХ МИКРОКОНТРОЛЛЕРОВ СЕМЕЙСТВА INTEL 8051 В ЧАСТИ КОМАНД ПЕРЕДАЧИ ДАННЫХ
Цель работы: Научиться создавать проекты в среде Keil μVision; изучить команды передачи данных микроконтроллеров семейства Intel 8051; научиться разрабатывать и отлаживать программы, использующие передачи данных с использованием различных методов адресации.
Команды передачи данных как правило содержат два операнда – место размещения источника данных (или сами данные) и место назначения (приемник). В командах передачи данных как правило один из операндов задан неявно (неявная адресация) или номером регистра (регистровая адресация), а другой операнд – непосредственно, или своим прямым адресом, или косвенно в одном из регистров. Исключение – команда записи прямоадресуемого байта по прямому адресу. Признаком косвенной адресации является знак @, стоящий у обозначения регистра, который содержит адрес операнда. Число, стоящее под знаком #, рассматривается как непосредственное данное. Ниже приведены примеры записи различных команд передачи данных на языке Ассемблера.
;Работа с внутренней памятью данных:
MOV PSW, #00h ;принудительно устанавливаем нулевой банк РОН
MOV A, #0AAh ;загружаем в аккумулятор непосредственное данное
MOV A, 030h ;загружаем в A прямоадресуемый (по адресу 030h) байт
MOV A, VAR1 ;загружаем в A прямоадресуемый байт ;(переменная по адресу VAR1)
MOV A, R3 ;копируем в аккумулятор содержимое регистра R3
MOV R0,#030h ;загружаем в регистр R0 непосредственные данные ;(косвенный адрес)
MOV A, @R0 ;копируем в аккумулятор данные по косвенному адресу
MOV R1, A ;копируем в R1 содержимое аккумулятора
MOV R2, #55h ;загружаем в R2 непосредственное данное
MOV R0, #VAR1 ; адрес переменной VAR1 -> в R0
MOV @R0, #055h ;непосредственные данные загружаем в косвенно ;(через R0) адресуемую переменную VAR1
MOV ANY_VAR, #0AAh ;загружаем в ANY_VAR непосредственные данные
MOV 030h, #0FFh ;загружаем по прямому адресу 030h ;непосредственные данные
MOV 030h, 032h ;загружаем по прямому адресу 030h ;прямоадресуемого (по адресу 032 h) байта
MOV 030h, ANY_VAR ;аналогично предыдущему, но с использованием ;автоматически распределенной переменной
XCH A, ANY_VAR ;обмен аккумулятора с прямоадресуемой ячейкой
XCH A, R1 ;обмен аккумулятора с регистром
XCH A, @R0 ;обмен аккумулятора с ячейкой по косвенному адресу
XCHD A, @R0 ;обмен младшими тетрадами аккумулятора с ячейкой по ;по косвенному адресу
MOV PSW, #08h ;принудительно устанавливаем первый банк РОН MOV PSW, #10h ;принудительно устанавливаем второй банк РОН MOV PSW, #18h ;принудительно устанавливаем третий банк РОН
MOV P0, #055h ;записываем непосредственное значение в порт 0
MOV P1, @R1 ;записываем в порт 1 байт, расположенный ;по косвенному адресу
MOV P2, 042h ;записываем в порт 2 прямоадресуемый байт
MOV P3, #0FFh ;записывем в регистр порта 3 все единицы, ;для того, чтобы иметь возможность
; считывать данные непосредственно с контактов порта MOV 042h, P3 ;читаем данные из порта 3 в ячейку памяти по прямому
MOV A, P3 ;читаем данные из порта 3 в аккумулятор
MOV @R0, P3 ;читаем данные из порта 3 в ячейку памяти по ;косвенному адресу
;Работа с внешней памятью данных:
MOV A, #000h ;загрузка в аккумулятор непосредственных данных ;которые будут скопированы во внешнюю память
MOV DPTR, #X_BYTE_ARRAY ;загрузка в DPTR непосредственных данных ;(16-разрядный адрес массива X_BYTE_ARRAY)
MOVX @DPTR, A ;косвенный доступ через DPTR
MOV DPTR, #X_BYTE_ARRAY+2 ;загрузка в DPTR непосредственных данных ;(адрес третьего элемента массива)
MOVX A, @DPTR ;обратная запись
;Чтение констант из памяти программ:
MOV DPTR, #C_BYTE_ARRAY ;загрузка в DPTR базового адреса массива ;констант в памяти программ
MOV A, #003h ;загрузка в аккумулятор непосредственных данных ;(индекса требуемой константы – в данном случае 3)
MOVC A, @A+DPTR ;чтение в аккумулятор константы из массива ;с базовым адресом, содержащемся в DPTR, ;по индексу, заданному в аккумуляторе.
Операции по передаче данных между памятью и регистрами общего назначения (и аккумулятором) взаимны. То есть если существует операция передачи байта из памяти в регистр, то существует и обратная операция. Единственное исключение – из памяти программ возможно только чтение.
При работе с внешней памятью данных и памятью программ, в обмене всегда участвует аккумулятор.
1.Изучить команды передачи данных в системе команд микроконтроллеров семейства Intel 8051.
2.Изучить методы ввода и вывода данных с использованием портов микроконтроллера.
3.Разработать исходный текст программы на языке Ассемблера, выполняющей следующую последовательность действий:
-загрузить число 0AAh в аккумулятор используя непосредственную адресацию операнда;
-скопировать содержимое аккумулятора в ячейку памяти, предварительно распределенную под внутреннюю переменную;
-скопировать число из внутренней переменной в один из регистров
общего назначения используя регистровую адресацию для регистра и прямую – для внутренней переменной;
-скопировать содержимое регистра общего назначения в ячейку внутренней памяти, используя косвенную адресацию ячейки памяти;
-изменить номер выбранного банка регистров общего назначения; -скопировать содержимое ячейки внутренней памяти в регистр общего
назначения в новом банке; -скопировать содержимое регистра общего назначения во внешнюю
память данных, используя аккумулятор для промежуточного хранения данных, и косвенный доступ к адресу ячейки во внешней памяти данных;
-скопировать содержимое ячейки внешней памяти данных в одну из ячеек внутренней памяти данных.
4.Разработать программу, осуществляющую преобразование четырехразрядного кода Грея в четырехразрядный двоичный код, используя операции чтения констант из памяти программ. Входные данные получить из порта 0 (Port 0) микроконтроллера, выходные выдать на порт 1 (Port 1).
Примечание: Четырехразрядный код Грея преобразуется в двоичный код в соответствии с таблицей.
1. Создать проект в среде Keil μVision, содержащий один файл исходного текста программы, реализующей последовательность действий по пункту 2 домашнего задания. Откомпилировать и отладить программу.
2. Создать новый проект в среде Keil μVision, реализующий преобразователь кода по пункту 3 домашнего задания. Откомпилировать и отладить программу кодопреобразователя.
Примечание: Вместо создания нового проекта разрешается добавлять новую функциональность в одну общую для всей лабораторной работы программу.
1.Перечислите виды адресных пространств микроконтроллеров семейства Intel 8051.
2.Какие виды адресации операндов используются в командах передачи
3.Перечислите возможные длины операндов, использующихся в командах передачи данных.
4.Какие регистры микроконтроллера используются для косвенной адресации операндов, расположенных в различных адресных пространствах?
5.Доступно ли для использования какое-либо адресное пространство микроконтроллера, если в нем не определено ни одного сегмента?
6.Какие способы адресации операндов используются для доступа к регистрам специальных функций микроконтроллера.
7.Какие способы адресации операндов могут быть использованы для доступа к регистрам общего назначения текущего выбранного банка?
8.Доступны ли программе ячейки внутренней памяти данных, занимаемые неиспользуемыми банками регистров общего назначения? Если доступны, то какими способами?
9.Каким образом обеспечить автоматическое распределение переменной (или константы) в памяти микроконтроллера по определенному физическому адресу.
10.Какой физический смысл имеют идентификаторы в сегментах, определенных во внутренней и внешней памяти данных?
Лабораторная работа № 2
ИЗУЧЕНИЕ СИСТЕМЫ КОМАНД И ПРИЕМОВ ПРОГРАММИРОВАНИЯ ОДНОКРИСТАЛЬНЫХ МИКРОКОНТРОЛЛЕРОВ СЕМЕЙСТВА INTEL 8051 В ЧАСТИ АРИФМЕТИЧЕСКИХ, ЛОГИЧЕСКИХ И БИТОВЫХ ОПЕРАЦИЙ
Цель работы: Изучить арифметические, логические операции и операции над отдельными битами микроконтроллеров семейства Intel 8051; ознакомиться с реализацией алгоритмов арифметических действий над многословными операндами; научиться разрабатывать и отлаживать программы, использующие арифметические, логические и битовые операции.
В системе команд микроконтроллеров семейства Intel 8051 доступны следующие виды операций над отдельными битами:
-Установка бита (в том числе бита переноса) в единицу; -Сброс бита (в том числе бита переноса) в ноль; -Инверсия бита (в том числе бита переноса); -Логическое И бита и переноса; -Логическое ИЛИ бита и переноса; -Логическое ИЛИ инверсии бита и переноса; -Копирование бита в перенос и наоборот.
Операции установки, сброса и инверсии переноса подразумевают его неявно, а для этих же операций с любым другим битом используется его прямой адрес.
Битовые операции возможны только над разрядами тех ячеек памяти (и SFR), которые допускают битовую адресацию.
Результат двухоперандных логических операций над битами сохраняется в бите переноса.
Далее приведены различные варианты записи операций над битами на языке Ассемблера:
SETB C ;установка переноса в единицу
CLR ANY_FLAG ;сброс битовой переменной ANY_FLAG
CPL 028h.2 ;инверсия второго бита байта, расположенного ;по прямому адресу байта 28h
ANL C, FLAG1 ;логическое И переменной FLAG1 и переноса ANL C, /042h ;логическое И инверсии бита, расположенного
;по прямому адресу бита 42h
;(адрес байта 28h, бит 2) и переноса
ORL C, P1.0 ;логическое ИЛИ нулевого бита порта 0 и переноса ORL C, /ANY_FLAG ;логическое ИЛИ инверсии переменной
;ANY_FLAG и переноса
MOV C, FLAG1 ;передача в перенос содержимого битовой ;переменной FLAG1
MOV 081h, C ;передача содержимого переноса в ;первый бит порта P0 (081h – абсолютный ;адрес первого бита порта 0)
Наиболее часто операции над битами используются для управления отдельными линиями портов ввода/вывода (для реализации различных алгоритмов обмена данными), а также для изменения содержания отдельных бит регистров специальных функций для изменения режимов работы периферийных устройств и при обслуживания прерываний.
Также операция сброса переноса СТРОГО НЕОБХОДИМА перед выполнением операции вычитания, так как вычитания без учета переноса в системе команд не предусмотрено.
Система команд микроконтроллеров семейства Intel 8051 реализует следующие арифметические действия:
-Сложение двух восьмиразрядных операндов (без учета и с учетом переноса), со знаком в дополнительном коде или беззнаковых;
-Сложение двух восьмиразрядных беззнаковых упакованных двоичнодесятичных чисел (BCD – Binary Coded Decimals) с последующей коррекцией результата;
-Вычитание двух восьмиразрядных операндов (только с учетом переноса !), со знаком в дополнительном коде или беззнаковых;
-Инкремент (увеличение на единицу) восьмиразрядного операнда (доступен инкремент 16-разрядного операнда – только для регистра DPTR);
-Декремент (уменьшение на единицу) восьмиразрядного операнда; -Умножение двух восьмиразрядных беззнаковых операндов, с
получением 16-разрядного результата; -Целочисленное деление восьмиразрядного беззнакового операнда (в
аккумуляторе) на восьмиразрядный беззнаковый операнд (в регистре B) с получением восьмиразрядного частного (в аккумуляторе) и восьмиразрядного остатка от деления (в регистре B).
Примеры реализации арифметических действий над однословными операндами приведены ниже:
;Сложение двух операндов:
MOV VAR1, #055h ;загрузка непосредственного значения в VAR1
MOV ANY_VAR, #03Ah ;загрузка непосредственного значения в ANY_VAR MOV A, VAR1 ;копирование первого операнда в аккумулятор
ADD A, ANY_VAR ;сложение содержимого переменной ;ANY_VAR с содержимым аккумулятора,
;результат – в аккумуляторе
;Вычитание двух операндов:
MOV A, VAR1 ;копирование уменьшаемого в аккумулятор CLR С ;ОБЯЗАТЕЛЬНЫЙ сброс переноса
SUBB A, #010h ;вычитание из содержимого аккумулятора ;непосредственного операнда (числа 10h=16d) ;результат — в аккумуляторе
;Сложение двух операндов в BCD-коде:
MOV A, #055h ;копирование первого операнда (непосредственное BCD ;данное 55) в аккумулятор
ADD A, #018h ;сложение содержимого аккумулятора с непосредственным ;данным (BCD 18), результат – в аккумуляторе
DA A ;двоично-десятичная коррекция содержимого аккумулятора
;Инкремент и декремент:
MOV ANY_VAR, #0AAh
INC ANY_VAR ;инкремент и декремент прямоадресуемого байта, DEC ANY_VAR ;заданного в переменной ANY_VAR
MOV DPTR, #01234h ;загрузка DPTR
INC DPTR ;инкремент двухбайтового регистра DPTR
;Умножение и деление:
MOV A, ANY_VAR ;загрузка операнда первого множителя в аккумулятор MOV B, #055h ;загрузка операнда второго множителя в регистр B MUL AB ; результат 03872h (B=38h, A=72h)
MOV A, ANY_VAR ; загрузка операнда делимого в аккумулятор MOV B, #052h ;загрузка операнда делителя в регистр B
DIV AB ;результат 02h в аккумуляторе, остаток 06h в регистре B
Арифметические действия над операндами, размер которых превышает один байт, реализуются программно.
Сложение и вычитание многословных операндов производится последовательно, начиная с младших байтов, причем младшие байты операндов складываются (вычитаются) без учета переноса, а все остальные – с учетом переноса. Аналогичным образом производится сложение чисел в BCD-коде. Примеры операций с многословными операндами приведены ниже.
;Сложение и вычитание трехсловных операндов:
;операнд 1 и результат в ячейках памяти 040h, 041h, 042h ;операнд 2 — в ячейках памяти 044h, 045h, 046h; ;результат — в ячейках памяти 048h, 049h, 04Ah; ;младший байт – по меньшему адресу
;операнд 1 равен 0335597h
;операнд 2 равен 0112286h
;начинаем операцию с младшего байта:
;продолжаем со средним:
;заканчиваем со старшим:
;начинаем операцию с младшего байта:
CLR C ;ОБЯЗАТЕЛЬНЫЙ сброс переноса
;продолжаем со средним:
;заканчиваем со старшим:
;также сложение чисел в BCD-коде:
Умножение многословных операндов (на примере двухбайтовых операндов) осуществляется в соответствии со следующим выражением:
M = A × B = ( a m × 256 + a l ) × ( b m × 256 + b l ) =
= a m × b m × 65536 + a l × b m × 256 + a m × b l × 256 + a l × b l ;
где a m , b m — старшие байты операндов A и B ;
a l , b l — соответственно младшие байты.
Фактически операция умножения двухбайтовых операндов вырождается в четыре операции умножения однобайтовых операндов и три операции сложения четырехбайтовых операндов, часть байтов которых равна нулю, как это показано на рисунке.
Рисунок Умножение двух двухбайтовых операндов
Как видно из рисунка, нулевой байт первого частичного произведения не складывается ни с одним из значащих байтов, следовательно он войдет в общее произведение без изменения. Причем отсутствие сложения влечет и отсутствие переноса в первый байт общего произведения. Следовательно сложению подвергаются только три байта с номерами 1, 2, 3.
Реализация алгоритма умножения двух двухбайтовых операндов приведена далее.
;умножение двухбайтовых операндов ;операнд 1 в регистрах R0(мл), R1(ст) ;операнд 2 в регистрах R2(мл), R3(ст)
;результат в регистрах R4(мл), R5, R6, R7(ст)
;работа с первым частичным произведением: MOV A, R0 ;загрузили в A младший байт оп 1
В системе команд микроконтроллеров семейства Intel 8051 также реализована группа команд, реализующих логические операции над байтами данных:
-логическое И двух операндов; -логическое ИЛИ двух операндов; -исключающее ИЛИ двух операндов; -инверсия аккумулятора; -обнуление аккумулятора;
-циклический сдвиг вправо(влево) аккумулятора; -сдвиг вправо(влево) аккумулятора через перенос; -обмен тетрад в аккумуляторе.
Одним из операндов и приемником результата операции как правило является аккумулятор. Исключение – первые три типа операций могут вместо аккумулятора использовать прямоадресуемый байт, но только в одном случае – если второй операнд – непосредственное данное.
Логические операции как правило используются для маскирования определенных бит в регистрах специальных функций. Маскирование – обычно это установка в единицу (или сброс в ноль) нескольких разрядов регистра, причем остальные разряды должны сохранить свои значения неизменными.
При маскировании отдельных бит регистров, логические операции становятся более выгодными по сравнению с битовыми операциями, в случае необходимости изменения сразу нескольких бит в одном регистре (одна команда изменяет сразу все биты).
Операции сдвига удобно использовать для быстрого умножения и деления чисел на степени двойки. Сдвиг на один разряд влево (вправо) эквивалентен умножению (делению) числа на два.
Далее приведены примеры использования логических операций на языке Ассемблера:
;Пример 1 ;Установка битов 0, 3, 5, 6 числа, размещенного во
;внутренней памяти по адресу 40h в единицу.
;то же самое с использованием аккумулятора:
;Пример 2 ;Сброс битов 1, 2, 4, 7 числа, размещенного во
;внутренней памяти по адресу 40h в ноль.
ANL 040h, #069h ;069h=01101001B
ANL 040h, #01101001B ;абсолютно эквивалентно ;предыдущей команде
;то же самое с использованием аккумулятора:
;Пример 3 ;Создать из двух чисел, хранимых в памяти, новое число,
;четные разряды которого содержат четные разряды первого ;числа, а нечетные разряды – нечетные разряды второго ;Число 1 – по адресу 040h, число 2 – по адресу 041h
ANL 040h, #0AAh ;выделили четные разряды числа 1 ;и обнулили нечетные
ANL 041h, #055h ;выделили нечетные разряды числа 2 ;и обнулили четные
MOV A, 040h ;скопировали число 1 в аккумулятор
ORL A, 041h ; получили в аккумуляторе искомое число MOV 042h, A ;сохранили его по прямому адресу 42h
;Пример 4 ;Разделить число длиной два байта (беззнаковое)
;на 8 (2^3). Число размещено во внутренней памяти ;данных по адресам 040h (мл часть) и 041h (ст. часть)
;сдвигаем старшую часть на один разряд вправо:
CLR C ;очищаем бит переноса
RR A ;сдвиг через перенос, младший разряд – в перенос
;сдвигаем младшую часть на один разряд вправо с учетом переноса:
RR A ;старший разряд вдвигаем из переноса, младший — теряем
;далее повторяем предыдущие операции еще раз:
1. Изучить арифметические, логические операции и операции над отдельными битами в системе команд микроконтроллеров семейства Intel 8051.
2. Разработать исходный текст программы на языке Ассемблера, выполняющей:
-сложение, вычитание однобайтовых чисел в дополнительном
-сложение двухбайтовых BCD-чисел; -умножение беззнаковых однобайтовых чисел;
-целочисленное деление беззнаковых однобайтовых чисел.
3. Разработать исходный текст программы на языке Ассемблера, вычисляющей следующее арифметическое выражение:
16 ; где A 16 – двухбайтовое число, размещенное в одном
из регистров выбранного банка РОН;
B 16 – двухбайтовое
внутренней памяти данных;
C 8 – однобайтовое
внутренней памяти данных;
D 16 – двухбайтовое число, размещенное во внешней памяти данных;
Примечание: Действия «+», «–», «∙», «/» в данном пункте считать арифметическими.
4. Разработать исходный текст программы, вычисляющий следующее выражение алгебры логики:
R = ( A × B ) + ( C × D ) ; где A , B , C , D – однобайтовые числа.
Числа должны быть представлены в выбранном банке РОН, во внутренней памяти данных, во внешней памяти данных, в памяти программ. Результат сохранить байтовую переменную, размещенную во внутренней памяти данных.
Примечание: Действия «+» и «∙» в данном пункте считать логическими.
1. Откомпилировать и отладить в среде Keil μVision программу реализующую арифметические действия по пункту 2 домашнего задания. Проверить правильность выполнения действий на нескольких примерах, результат которых занести в отчет по лабораторной работе.
2. Откомпилировать и отладить в среде Keil μVision программу реализующую вычисление арифметического выражения по
пункту 3 домашнего задания. Проверить правильность функционирования программы.
3. Откомпилировать и отладить в среде Keil μVision программу реализующую вычисление выражения алгебры логики по пункту 4 домашнего задания. Проверить правильность функционирования программы.
1.Каким образом осуществляется сложение и вычитание многословных операндов в дополнительном коде?
2.Каким образом осуществляется сложение многословных BCD операндов?
3.В чем заключается сущность двоично-десятичной коррекции результата сложения?
4.Как осуществить умножение беззнаковых многословных операндов? 5.Можно ли использовать операцию байтового целочисленного деления
для деления многословных чисел?
6.В каком случае для маскирования определенных бит в регистрах выгодно использовать логические операции? В каком случае битовые операции выгоднее?
7.Где сохраняется результат при выполнении арифметических операций? Логических операций? Есть ли исключения?
8.Как наиболее просто выполнить умножение или деление числа на степень двойки?
9.Какое максимальное число сдвигов необходимо выполнить, чтобы умножить или разделить число на любую степень двойки?
Лабораторная работа № 3
ИЗУЧЕНИЕ ПРИЕМОВ ПРОГРАММИРОВАНИЯ РАЗВЕТВЛЯЮЩИХСЯ И ЦИКЛИЧЕСКИХ АЛГОРИТМОВ С ИСПОЛЬЗОВАНИЕМ ОПЕРАЦИЙ УПРАВЛЕНИЯ ВЫЧИСЛИТЕЛЬНЫМ ПРОЦЕССОМ В СИСТЕМЕ КОМАНД ОДНОКРИСТАЛЬНЫХ МИКРОКОНТРОЛЛЕРОВ СЕМЕЙСТВА INTEL 8051
Цель работы: Изучить операции управления вычислительным процессом в системе команд микроконтроллеров семейства Intel 8051; ознакомиться с реализацией разветвляющихся и циклических алгоритмов; научиться разрабатывать и отлаживать программы, содержащие ветвления и циклы.
На блок-схемах программ разветвляющиеся алгоритмы могут быть изображены различным образом. Один из вариантов представления приведен на рисунке (OP – операторы языка программирования).
Рисунок Блок-схема разветвляющегося алгоритма (изображение 1)
Такое представление не противоречит действующим стандартам (ГОСТ 19.701-90 Схемы алгоритмов, программ, данных и систем), но неудобно при непосредственной разработке программ, особенно при использовании машинно-ориентированных языков программирования (Ассемблер). Это обусловлено непрерывностью памяти микроконтроллеров.
С точки зрения удобства программирования на машинноориентированном языке, такой алгоритм наиболее удобно представлять в следующем виде:
Рисунок Блок-схема разветвляющегося алгоритма (изображение 2)
Такое представление учитывает размещение операторов в памяти микроконтроллера и позволяет легко перейти к непосредственной записи алгоритма на языке. С другой стороны, легко убедиться, что данное представление абсолютно аналогично предыдущему, оно просто более удобно для разработки на его основе программы на языке Ассемблера. Скачок программы, обходящий OP3, реализуется при помощи операции безусловного перехода. Обход оператора OP2 в случае невыполнения условия – при помощи операции условного перехода.
Для программирования разветвляющихся и циклических алгоритмов в системе команд микроконтроллеров семейства Intel 8051 реализованы несколько видов команд переходов.
LJMP – длинный абсолютный безусловный переход, второе поле
команды (второй и третий байты) содержит адрес в памяти программ, на который осуществляется переход;
AJMP – средний абсолютный безусловный переход внутри
двухкилобайтной страницы, часть адреса перехода (три старших бита) содержится в коде операции, а остальные – во втором байте команды;
SJMP – короткий относительный переход в пределах смещения
-128. +127 адресов в памяти программ, которое содержится во втором байте команды в форме знакового числа в дополнительном коде, при этом переход
осуществляется не по абсолютному адресу, а относительно текущего значения счетчика программ;
JMP @A+DPTR – косвенный абсолютный длинный переход;
Кроме того, при программировании на языке ассемблера в среде Keil μVision, мнемоники команд LJMP , AJMP , SJMP можно заменять на единую
JMP , в этом случае при ассемблировании файла исходного текста, она будет
заменена на фактическую, в зависимости от фактической длины перехода. Естественно возникновение вопроса: каким образом рассчитать
фактические адреса для команд «длинного» и «среднего» переносов и смещения для «короткого»? Ответ прост: никак! При программировании команд переходов (за исключением перехода по косвенному адресу) программист как правило использует не фактические адреса и смещения, а символические метки, которые преобразуются в адреса и смещения в процессе ассемблирования и линкования. Это позволяет программисту не задумываться о конкретных адресах и уменьшает возможность возникновения ошибок. Переход по косвенному адресу ( JMP @A+DPTR ) предполагает предварительную запись в регистры DPTR и A
фактических значений адреса и смещения, которые ассемблером никак не обрабатывается.
Примечание: Никто не запрещает указывать в командах переходов и фактические значения адресов и смещений.
Переходы по условию:
JZ – переход, если аккумулятор равен нулю; JNZ – переход, если аккумулятор не равен нулю; JC – переход, если бит переноса установлен; JNC – переход, если бит переноса сброшен;
JB – переход, если заданный в команде бит установлен;
JNB – переход, если заданный в команде бит сброшен;
JBС – переход, если заданный в команде бит установлен, при этом анализируемый бит сбрасывается.
Кроме простых команд, выполняющих только переход по условию, существует и группа команд, выполняющих какое-либо действие (декремент или сравнение), и по его результатам выполняющее переход. К ним относятся:
DJNZ – декремент регистра или прямоадресуемого байта и переход,
если результат не равен нулю (используется как правило для организации циклов);
CJNE – сравнение аккумулятора, регистра или косвенно-адресуемого
байта с прямоадресуемым байтом или непосредственным значением и переход, если они не равны.
Все команды переходов по условию – относительные, то есть переход осуществляется в пределах -128 . +127 адресов в памяти программ,
относительно места расположения команды перехода.
Примеры программирования разветвляющихся и циклических алгоритмов с использованием команд переходов приведены ниже:
;Пример 1 ;Разветвляющийся алгоритм
;Запись во внешнюю память данных ;числа 055h по четному адресу, ;а AAh – по нечетному.
;адрес числа находится в DPTR
MOV A, DPL ;копируем младшую часть DPTR ;в аккумулятор, так как DPTR
;не бит-адресуемый, а аккумулятор – да
JB ACC.0, L0_0 ;если ACC.0=1, то адрес нечетный,
;и переход на метку L0_0, в противном ;случае адрес четный и перехода нет,
;выполняем запись 55h MOV A,#55h ;загружаем в A число 55h
MOVX @DPTR,A ;выводим его во внешнюю память данных JMP L0_1 ;обходим участок программы, выводящий данные
;в нечетный адрес
L0_0: MOV A,#0AAh ;загружаем в A число AAh
MOVX @DPTR,A ;выводим его во внешнюю память данных
;конец примера 1, продолжение программы .
;Пример 2 ;Цикл с постусловием.
;Заполнение массива ячеек во внешней памяти данных ;константой.
;базовый адрес массива – в R0(мл.),R1(cт.); ;число элементов в массиве – в R7; ;значение константы задано в R6
MOV DPL,R0 ;загружаем базовый адрес
MOV DPH,R1 ;в DPTR, с которым и будем работать MOV B,R7 ;загружаем число элементов массива в B,
;чтобы не уничтожать исходные данные
MOV A,R6 ;константу – в аккумулятор (только из него
;можно выводить во внешнюю память данных) L1_0: MOVX @DPTR,A ;выводим во внешнюю память данных
INC DPTR ;инкрементируем DPTR (переходим на следующий адрес) DJNZ B, L1_0 ;декрементируем B, и если в результате
;он не равен нулю, то делаем переход на ;начало цикла, в противном случае выходим из ;цикла (не делая перехода на начало)
;конец примера 2, продолжение программы .
;Пример 3 ;Цикл с предусловием.
;Заполнение массива ячеек во внешней памяти данных
;константой. Исходные данные – как в примере 2
MOV B,#0 ;обнуляем переменную цикла
L2_0: MOV A,B ;загружаем переменную цикла в аккумулятор
CJNE A,R7,L2_1 ;если переменная цикла равна длине массива, ;то выходим из цикла
MOV A,R6 ;загружаем в А константу для вывода
MOVX @DPTR,A ;выводим во внешнюю память данных
INC DPTR ;инкрементируем DPTR (переходим на следующий
INC B ;инкрементируем переменную цикла
JMP L2_0 ;зацикливаемся
;конец примера 3, продолжение программы .
Как видно из приведенных примеров, реализация цикла с предусловием занимает больше кода и требует больше времени, чем реализация цикла с постусловием. Циклы как первого, так и второго вида широко применяются в практике программирования, но циклы с постусловием все же более удобны для реализации на языке ассемблера.
Типовые алгоритмы как правило содержат как ветвления, так и циклы. При этом циклы могут содержать в своем теле разветвляющиеся алгоритмы, а отдельные ветви программы содержать в себе циклы.
Для примера рассмотрим типовую задачу – поиск наибольшего модуля в массиве чисел со знаком, представленных в дополнительном коде.
Как известно, значение положительного числа в дополнительном коде непосредственно представляет его модуль, поэтому в случае положительного числа никаких действий по вычислению его модуля делать не приходится. Для отрицательных чисел в дополнительном коде, модуль может быть рассчитан путем инвертирования всех его разрядов, а затем арифметического сложения полученного результата с единицей.
Таким образом, алгоритм вычисления модуля числа в дополнительном коде является разветвляющимся.
Для решения поставленной задачи модуль числа должен быть вычислен для каждого элемента массива – то есть в наличии имеется цикл. Каждое полученное значение модуля сравнивается с предыдущим найденным значением максимального модуля, и замещает последнее в случае, если оно меньше. Естественно, в начале алгоритма максимальное найденное должно быть равно нулю (нуль имеет минимальный возможный модуль). Следовательно здесь имеется еще одно ветвление алгоритма (замещать или не замещать).
Полностью алгоритм поиска приведен на рисунке.
Рисунок Блок-схема алгоритма поиска максимального модуля в массиве байт
На блок-схеме алгоритма обозначены:
массив байтов в памяти данных;
основная переменная, с которой проводятся вычисления (в
запомненное значение максимального модуля (R2) –
временное хранилище для модуля текущего элемента
переменная цикла (в программе – SFR B).
Фрагмент программы, реализующей алгоритм поиска приведен ниже.
;Программа поиска числа с максимальным модулем в массиве ;байт, размещенном во внешней памяти данных.
;Базовый адрес массива – в R0(мл.),R1(cт.);
;число элементов в массиве – в R7 MOV DPL,R0 ;загружаем базовый адрес
MOV DPH,R1 ;в DPTR, с которым и будем работать MOV B,R7 ;загружаем число элементов массива в B MOV R2,#0 ;обнуляем максимальное значение модуля в
L0_0: MOVX A,@DPTR ;загружаем текущий элемент массива из
;внешней памяти данных
JNB ACC.7,L0_1 ;если старший бит числа равен нулю, то ;обходим вычисление модуля отрицательного ;числа
;Вычисляем модуль отрицательного числа: CPL A ;инвертируем биты числа
INC A ;прибавляем единицу к содержимому аккумулятора
L0_1: MOV R5,A ;R5 – временное хранилище для текущего модуля CLR C ;очищаем бит переноса
SUBB A,R2 ;вычитаем из A значение предыдущего ;запомненного модуля
JB ACC.7, L0_2 ;если получилась отрицательная разница, ;то текущий модуль меньше запомненного, ;а следовательно копировать не нужно
MOV 02,R5 ;копируем R5 в R2 (расположен по прямому ;адресу 02), так как операции MOV RY,RX — нет
DJNZ B, L0_0 ;зацикливаем программу
1. Изучить операции безусловных и условных переходов в системе команд микроконтроллеров семейства Intel 8051.
2. Оформить в виде законченного программного модуля исходный текст программы на языке Ассемблера, выполняющей
заполнение массива во внешней памяти данных числами 55h по четным адресам и AAh – по нечетным.
3. Оформить в виде законченного программного модуля исходный текст программы на языке Ассемблера, выполняющей заполнение массива во внешней памяти данных константой, заданной в одном из РОН. Задание выполнить для цикла с постусловием и для цикла с предусловием.
4. Оформить в виде законченного программного модуля исходный текст программы на языке Ассемблера, выполняющей поиск наибольшего модуля в массиве чисел со знаком, представленных в дополнительном коде.
5. Разработать исходный текст программы, выполняющей подсчет количества нулевых и единичных бит в массиве байт, расположенных во внутренней памяти данных. Размер массива –
не более 31 байта.
Примечание: Использовать для выделения отдельных битов в байте операцию сдвига. Сдвиг осуществить в цикле (восемь итераций). Общий подсчет выполнить также в цикле (внешний по отношению к циклу со сдвигом).
1. Откомпилировать и отладить в среде Keil μVision программу, реализующую заполнение массива во внешней памяти данных числами 55h по четным адресам и AAh – по нечетным. Проверить правильность функционирования программы.
2. Откомпилировать и отладить в среде Keil μVision программу, выполняющую заполнение массива во внешней памяти данных константой, заданной в одном из РОН, используя цикл с постусловием и цикл с предусловием. Проверить правильность выполнения программы для различных значений константы и различных значений объема массива. Определить на основе анализа результатов выполнения программы, чем отличаются циклы с предусловием от циклов с постусловием.
3. Откомпилировать и отладить в среде Keil μVision программу, выполняющую поиск наибольшего модуля в массиве чисел со знаком, представленных в дополнительном коде. Проверить правильность функционирования программы на различных примерах. Результаты проверок привести в отчете.
4. Откомпилировать и отладить в среде Keil μVision программу, выполняющую подсчет количества нулевых и единичных бит в массиве байт, расположенных во внутренней памяти данных. Размер массива – не более 31 байта. Проверить правильность выполнения программы для различных наборов данных (различные элементы массива, различная длина массива). Результаты проверок привести в отчете.
1. Какие команды безусловных переходов представлены в системе команд микроконтроллеров семейства Intel 8051?
2. Чем отличаются «длинные» ( LJMP ), «средние» ( AJMP ) и «короткие» ( SJMP ) безусловные переходы?
3.Можно ли при программировании на ассемблере использовать «длинный» безусловный переход вместо короткого? Если да, то чем «короткий» переход лучше?
4. Можно ли в системе команд Intel 8051 реализовать переход по адресу, который не известен во время компиляции программы? Если да, то каким образом?
5. Есть ли в системе команд семейства Intel 8051 переходы по условию «больше», «больше или равно», «меньше», «меньше или равно»? Как реализуются при программировании на ассемблере Intel 8051 ветвления по перечисленным условиям?
6. Есть ли в системе команд семейства Intel 8051 специальные команды для организации циклических вычислений? При помощи каких команд организуются циклические вычисления при программировании на языке ассемблера?
7.Как при программировании на ассемблере реализуются циклы с предусловием (с постусловием)? В чем основное отличие цикла с предусловием от цикла с постусловием?
8. Какую максимальную длину (в количестве адресов) может занимать в памяти программ цикл с предусловием (с постусловием), для того, чтобы он мог быть зациклен командой условного перехода? Можно ли реализовать цикл большей длины? Если да, то как?
9. Существуют ли в системе команд микроконтроллеров семейства Intel 8051 ограничения на количество вложенных циклов? Если да, то какие?
Лабораторная работа № 4
ИЗУЧЕНИЕ ПРИЕМОВ ПРОГРАММИРОВАНИЯ ПОДПРОГРАММ И ОБРАБОТКИ ПРЕРЫВАНИЙ В СИСТЕМЕ КОМАНД ОДНОКРИСТАЛЬНЫХ МИКРОКОНТРОЛЛЕРОВ СЕМЕЙСТВА INTEL 8051
Цель работы: Изучить операции управления вычислительным процессом, направленные на организацию процедур (синхронных подпрограмм), подпрограмм обработки прерываний (асинхронных подпрограмм). Научиться разрабатывать синхронные подпрограммы, передавать в них аргументы из вызывающей программы различными способами и получать результат выполнения. Научиться разрабатывать и отлаживать подпрограммы-обработчики прерываний, сохранять контекст задачи при обработке прерываний, маскировать отдельные прерывания.
При разработке программ, потребность в выполнении определенных последовательностей действий может возникать множество раз и во множестве мест программы. К таким действия можно отнести: арифметические действия над многословными операндами (например сложение, вычитание, умножение, деление и др.), преобразование данных из одного вида в другой (например преобразование из двоичного кода в двоично-десятичный или ASCII и наоборот), ввод/вывод данных через внешние интерфейсы и многие другие. Попытки многократно вставлять необходимые последовательности команд в нужные места программы не выдерживают никакой критики: возможно возникновение ошибок, при необходимости изменить последовательность действий необходимо изменять ее во множестве мест, наконец неэффективно расходуется весьма дорогостоящий ресурс системы – память программ.
Для организации многократно повторяющихся вычислений в микропроцессорах используется механизм подпрограмм – специальным образом оформленных фрагментов кода, обеспечивающих их запуск (вызов) из любой точки программы, выполнение и возврат управления в строку программы, следующую непосредственно за вызывавшей (адрес возврата).
Адрес возврата на время выполнения подпрограммы сохраняется в специальной области памяти, носящей название СТЕК (stack — пакет). Стек действует по принципу «первым вошел – последним вышел» («First In – Last Out» — FILO). То есть данное, помещенное в стек первым, будет извлечено из него последним и наоборот.
В микроконтроллерах семейства Intel 8051 стек расположен во внутренней памяти данных. Его конкретное местоположение определяется значением адреса, хранящегося в указателе стека (Stack Pointer – SP – один из SFR) до первой фактической записи в стек. Значение в указателе стека указывает на последнюю использованную ячейку стека. Если в стек еще ничего не записывалось, то он указывает на ячейку памяти, расположенную непосредственно перед стеком. После аппаратного сброса микроконтроллера указатель стека содержит значение 07, это означает, что стек начинается с адреса 08 (начало второго банка РОН). Указатель стека может быть в любой момент записан новым значением, но обычно это производится один раз непосредственно после запуска программы, причем в указатель стека записывается значение, указывающее на его фактическое начало (на единицу меньше, если быть точным).
При каждой последующей записи в стек, указатель стека увеличивается. Стек «растет» «вверх». Так как вся внутренняя память данных в микроконтроллерах семейства Intel 8051 ограничена 128 ячейками, размеры стека, реализуемые на данных микроконтроллерах также невелики. Обычно используется стек размером 16 байт, иногда – 32. Это накладывает ограничения на степень вложенности подпрограмм, число и размер их аргументов, передаваемых через стек, объем сохраняемого контекста задачи (при обработке прерываний).
Подпрограммы, вызываемые из основной программы (или другой подпрограммы) микроконтроллера являются синхронными, так как их вызов выполняется непосредственно самой программой в строго синхронный с выполнением программы момент времени. При программировании на языке Ассемблера, синхронные подпрограммы обычно называются процедурами.
Вызывая процедуру, вызывающая программа может передать ей аргументы, а процедура, в свою очередь – возвратить в программу результат своего выполнения. Существуют различные способы передачи аргументов в процедуры и различные способы возврата результата.
В отличие от процедур, подпрограммы–обработчики прерываний вызываются аппаратными средствами микроконтроллера в момент возникновения необходимости выполнения действий с внешними устройствами. Естественно, эти моменты времени НИКАК НЕ СИНХРОНИЗИРОВАНЫ с выполнением основной программы микроконтроллера. Подпрограмма обработки прерывания может быть вызвана В ЛЮБОЙ МОМЕНТ. Поэтому подпрограммыобработчики прерываний часто называют асинхронными подпрограммами.
Так как подпрограмма обработки прерывания прерывает основную программу микроконтроллера в любой момент времени, основная программа не может передать в подпрограмму обработки прерывания никаких аргументов, кроме того подпрограмма обработки прерывания не может возвратить в основную программу никакого результата своего выполнения. После выполнения, программа обработчик прерывания должна оставить систему в точности в том состоянии, в котором та находилась до вызова подпрограммыобработчика. Это состояние носит название контекст задачи.
В более узком смысле под контекстом задачи понимается совокупность регистров микроконтроллера, которые могут быть изменены в ходе обработки прерывания. Существует два способа сохранения контекста задачи:
1) Сохранение контекста задачи в стек;
2) Переключение текущего банка регистров с нулевого, на другой и работа в процессе обработки прерывания с регистрами выбранного банка.
Первый способ универсален и работает всегда, во всех процессорах и микроконтроллерах, для любых регистров. Недостаток – затраты стекового пространства.
Второй способ в микроконтроллерах семейства Intel 8051 позволяет сохранить неизменными только восемь РОНов (R0…R7). Регистры специальных функций не имеют в данных микроконтроллерах альтернативных наборов, следовательно они должны быть сохранены в стек – в этом недостаток данного способа. Преимущество данного способа – в его скорости, так как никакого копирования данных не происходит, основная программа работает со своим банком РОН, а обработчик прерывания – со своим, доступным мгновенно.
Процедуры (синхронные подпрограммы)
При программировании на языке ассемблера семейства Intel 8051, каждая процедура должна предваряться меткой, которая используется для ее вызова. Эта метка носит название «точка входа».
Сохранение адреса возврата в стеке сохраняется вызывающей программой в момент вызова процедуры.
Извлечение адреса возврата из стека производится последней командой процедуры, в качестве которой всегда используется команда возврата из подпрограммы RET .
Вызов процедуры осуществляется командой LCALL или ACALL . Длина
команд аналогична «длинному» и «среднему» безусловным переходам. Команда сохраняет в стеке полный адрес возврата и выполняет переход на адрес точки входа процедуры. Команды LCALL и ACALL могут быть заменены на CALL ,
которая при компиляции будет заменена на необходимую.
Далее приведен скелет простейшей процедуры, которая не получает от вызывающей программы никаких аргументов и не возвращает результата, там же приведен код ее вызова.
SIMPLE_PROC: ;строка с меткой – точка входа, уже может ;содержать код, выполняющий необходимые действия ;действия
;последовательность операций процедуры .
;Далее завершение процедуры:
;Далее приводим вид вызова процедуры из основной программы:
Как видим, сама процедура обязательно должна содержать в своем теле только две строки: метку точки входа и строку возврата из процедуры ( RET ).
Если процедура в результате своего выполнения получает результат, который должен быть возвращен в основную программу, он обычно возвращается в регистрах общего назначения (R0…R7) текущего банка. При этом однобайтовый результат сохраняется в R7, двухбайтовый – в R6, R7 и т.д. Также для возврата результата могут быть использованы аккумулятор, его расширитель (регистр B), DPTR.
Это накладывает следующие особенности на использование РОН в программах: не следует считать, что после выполнения процедуры, содержимое РОН, регистров A, B, DPTR останется неизменным. Общепринятая практика программирования – не использовать регистры микроконтроллера для постоянного хранения данных. Их необходимо размещать во внутренней и внешней памяти данных.
Вызывающая программа может передать в процедуру аргументы. Это может быть сделано различными способами. Во-первых, аргументы могут быть переданы через регистры общего назначения микроконтроллера, во-вторых переданы через стек, в-третьих через глобальные переменные.
Примечание: Глобальные переменные – распределенные во внутренней или внешней памяти данных переменные, адрес которых доступен в пределах одного или нескольких модулей исходного кода программы.
Передача аргументов через глобальные переменные имеет один известный недостаток: невозможность рекурсивного вызова таких подпрограмм. Однако, малые размеры стека в микроконтроллерах и без того ограничивают возможность вложения процедур, а без использования рекурсии такой способ передачи аргументов оказывается весьма эффективным, к тому же не тратит пространство стека.
Передача аргументов через стек позволяет осуществлять рекурсивные вызовы функций, но требует наибольших затрат пространства стека. Передача аргументов через стек обычно используется в компиляторах языков высокого уровня для персональных ЭВМ и больших ЭВМ, где размеры стекового пространства достаточно велики. С передачей аргументов через стек тесно связано и распределение памяти для локальных переменных процедуры. Они также могут распределяться в пространстве стека, что приводит к еще большим затратам памяти. Кроме того, при передаче аргументов через стек, вызывающая программа перед вызовом процедуры заносит их в стек, а после выполнения процедуры должна освободить стек от них. Освобождение стека от использованных аргументов увеличивает потребное время на вызов процедуры.
Вместе с тем, распределение локальных переменных в стеке позволяет выполнять рекурсивные вызовы таких процедур (такие процедуры носят название реэнтерабельных — повторновходимых).
Передача аргументов через регистры общего назначения микроконтроллера считается в настоящее время наиболее эффективной и быстрой. Аналогично возврату результата, аргументы обычно передаются в регистрах R0…R7 текущего банка. При этом однобайтовый результат передается в R7, двухбайтовый – в R6, R7 и т.д. Для передачи адресов (указатели) используются регистры R0, R1. При этом, в качестве локальных переменных процедур, используются свободные (и освобождающиеся) от аргументов регистры.
Так как способы передачи аргументов, возвращения результата и распределения локальных переменных могут быть различными и должны быть взаимно согласованы между разработчиками основной программы и процедур, они носят название «соглашения о вызовах» («Calling convention»).
Рассмотрим примеры программирования процедур с различными способами передачи аргументов, распределения локальных переменных и возврата результата.
;Процедура установки портов P0 и P2 в единицу ;Аргументов – Нет
;Результат – не возвращается
INIT_IOPORTS: MOV P0,#0FFh MOV P2, #0FFh RET
;Пример 2 ;Процедура программной задержки переменной длительности.
;Передача аргумента через глобальную переменную. ;В качестве аргумента используется
;глобальная переменная DELAY_CNTR1;
;в нее предварительно заносится значение от 000h до 0FFh.
;также используется глобальная переменная DELAY_CNTR2.
MOV DELAY_CNTR2, #0FFH ;инициализируем счетчик
;вложенного цикла DELAY_VAR_LOOP_IN: ;начало тела вложенного цикла
DJNZ DELAY_CNTR2, DELAY_VAR_LOOP_IN ;конец тела вложенного
DJNZ DELAY_CNTR1, LONG_VAR_DELAY ;конец тела внешнего
;цикла, его начало ;совпадает с началом ;процедуры
RET ;выход из процедуры
MOV DELAY_CNTR1,#00Fh ;загружаем в DELAY_CNTR1 число
;циклов задержки (аргумент)
CALL LONG_VAR_DELAY ;вызываем процедуру
;Пример 3 ;Процедура программной задержки переменной длительности.
;Передача аргумента через глобальную переменную.
;При выполнении процедуры содержимое DELAY_CNTR1 НЕ ИЗМЕНЯЕТСЯ! ;Так как фактически в качестве переменной цикла используется ;ЛОКАЛЬНАЯ ПЕРЕМЕННАЯ В СТЕКЕ, в которую и копируется DELAY_CNTR1. ;Это позволяет задавать время задержки централизованно
;для всей программы без ее перекомпиляции.
PUSH DELAY_CNTR1 ;копируем счетчик цикла из глобальной ;переменной в стек, отводя под нее ;тем самым локальную переменную.
;SP теперь на нее и указывает.
MOV R0, SP ;копируем SP в R0 для последующего косвенного
;доступа в локальной переменной SHORT_VAR_DELAY_LOOP: ;начало тела цикла задержки
DEC @R0 ;декрементируем счетчик цикла в стеке по
CJNE @R0,#0,SHORT_VAR_DELAY_LOOP ;конец тела цикла задержки
DEC SP ;освобождаем стек от локальной переменной RET ;выходим из процедуры
MOV DELAY_CNTR1,#00Fh ;загружаем в DELAY_CNTR1 число ;циклов задержки (аргумент), например ;в начале выполнения программы
CALL SHORT_VAR_DELAY ;вызываем процедуру
;Пример 4 ;Процедура сложения однобайтовых чисел
;Передача аргументов через стек ;Возвращаемый результат в R7
ADD8: MOV A,SP ;загружаем указатель стека в аккумулятор
CLR CY ;обязательная очистка переноса перед вычитанием SUBB A,#02h ;вычитаем 2 из вершины стека, получаем
;местоположение первого аргумента процедуры MOV R0, A ;сохраняем первый аргумент в R0 для косвенного
DEC A ;получаем местоположение второго аргумента
MOV R1, A ;сохраняем второй аргумент в R1 для косвенного ;доступа
MOV A, @R0 ;копируем первый аргумент в А
ADD A, @R1 ;складываем первый аргумент в аккумуляторе со ;вторым
MOV R7, A ;копируем результат в регистр результата RET ;возвращаемся из процедуры
MOV A,#022h ;загрузка в стек второго
PUSH ACC ;аргумента процедуры
MOV A,#011h ;загрузка в стек первого
PUSH ACC ;аргумента процедуры
CALL ADD8 ;собственно вызов процедуры
DEC SP ;очистка стека
DEC SP ;продолжение очистки стека
Обработчики прерываний (асинхронные подпрограммы)
Точки входа подпрограмм-обработчиков прерываний, должны размещаться в памяти программ микроконтроллера по строго определенным адресам – векторам прерываний: 0003h, 000Bh, 0013h, 001Bh и 0023h (для базовой модели Intel 8051). К тому же, на каждый вектор приходится всего восемь ячеек памяти. Это накладывает определенные особенности на реализацию подпрограмм обработки прерываний на языке Ассемблера (да и на любых других языках программирования тоже). Обычно точка входа процедуры обработки прерывания размещается по адресу вектора прерывания, но в этой же строке производится передача управления (командой безусловного перехода) на фактическое тело процедуры обработки прерывания, размещенное в любом месте памяти программ.
Для того, чтобы реализовать обработку прерываний на микроконтроллере Intel 8051, помимо написания и правильного размещения собственно процедур обработки прерываний, необходимо также разрешить обработку прерываний в регистре маски прерываний, распределить приоритеты различных источников прерываний необходимым образом и установить необходимый тип фиксации прерывания (по фронту или по уровню).
Для примера разработаем программу, обрабатывающую прерывания от двух источников – внешних входов прерываний EXT0 и EXT1. Ниже приведен скелет исходного модуля такой программы. Обратите внимание на размещение процедур в памяти и на предварительные установки регистров маски, приоритетов прерываний, TCON (устанавливается фиксация запросов).
DSEG AT 30h ;Определяем сегмент данных во внутренней памяти данных
;по абсолютному адресу 30H
STACK: DS 10h ;зарезервировали 16 байт под стек ;(в начале сегмента данных)
;Отводим память под другие переменные в сегменте данных:
;Определяем сегмент кода, который начинается по нулевому адресу:
LJMP START ;переход по адресу метки START, ;на фактическое начало программы
;В начале памяти программ располагается таблица векторов ;прерываний:
;Вектор внешнего прерывания
;устанавливаем принудительно счетчик адреса
;на адрес вектора
LJMP INT_EXT0 ;сам обработчик находится не здесь,
адресу INT_EXT0 с целью обеспечения
;вызова обработчиков для каждого из
;Вектор внешнего прерывания
ORG 40h ;основная программа смещена относительно ;начала памяти программ на 40H = 64 байта, ;чтобы обойти таблицу векторов прерываний
START: MOV SP,#02Fh ;загружаем указатель стека на 02Fh (начало ;стека с 030h) дальше стек растет вверх
MOV IE, #085h ; разрешаем прерывания от EXT0 и EXT1
MOV IP, #000h ;устанавливаем приоритеты прерываний
MOV TCON,#000h ;сначала устанавливаем фиксацию обоих ;прерываний по уровню
SETB IT0 ;затем изменяем прерывание EXT0 — по фронту
MAIN_LOOP: NOP ;это основной цикл программы, он в данном случае
NOP ;ничего не делает
;Далее следуют описания подпрограмм обработки прерываний ;Местоположение в памяти программ — не важно.
;Подпрограмма обработки прерывания EXT0:
;Прерывание с фиксацией по фронту
PSW ;сохранение контекста задачи
;Действия по обработке прерывания. Если при обработке
;изменяется содержимое каких-либо регистров, ;то необходимо сохранить в стеке все изменяемые ;регистры. Например:
INC CNTR1 ;инкрементировали глобальную переменную
PSW ;восстановление контекста задачи
;возврат из обработки прерывания
;Подпрограмма обработки прерывания EXT1: ;Прерывание с фиксацией по уровню
INT_EXT1: PUSH PSW ;сохранение контекста задачи ;обработка прерывания, например:
MOV CNTR1,#00 ;обнулили глобальную переменную POP PSW ;сохранение контекста задачи
RETI ;возврат из обработки прерывания
END ;конец модуля
1.Изучить операции вызова подпрограмм и возврата из них в системе команд микроконтроллеров семейства Intel 8051
2.Ознакомиться с примерами программирования процедур на языке Ассемблера. Разобраться с различными способами передачи аргументов в подпрограммы, организации локальных переменных, возврата результата.
3.Оформить в виде законченной программы на языке Ассемблера примеры процедур, приведенные в методических рекомендациях. Снабдить основную программу кодом, вызывающим процедуры с передачей им аргументов и возврата результата.
4.Оформить в виде процедуры алгоритм, выполняющий поиск наибольшего модуля в массиве чисел со знаком, представленных в дополнительном коде, размещенном во внешней памяти данных. Аргументы (базовый адрес массива и число элементов в нем) передавать через регистры, результат (максимальный модуль) возвращать через регистры, локальные переменные организовать в регистрах. Реализовать программу, вызывающую данную процедуру, передающую в нее аргументы и принимающую результат.
5. Изучить реализацию подпрограмм обработки прерываний на языке Ассемблера. Разобраться с маскированием прерываний, кодированием приоритетов, возможностью вложения прерываний, фиксацией запросов.
6. Оформить исходный текст программы, обрабатывающей прерывания от двух источников – внешних входов прерываний EXT0 и EXT1.
1. Создать и откомпилировать в среде
реализующий различные примеры программирования
домашнего задания. Отладить программу, запуская ее на исполнение в пошаговом режиме с различными наборами исходных данных и наблюдая за изменением содержимого регистров микроконтроллера, локальных и глобальных переменных, последовательностью вызова, исполнения и возврата из процедур.
2. Создать и откомпилировать в среде Keil μVision проект, реализующий алгоритм поиска наибольшего модуля в массиве чисел в соответствии с требованиями пункта 4 домашнего задания. Отладить программу, запуская ее на выполнение в пошаговом режиме с различными наборами исходных данных. Проверить правильность функционирования программы для различных наборов исходных данных.
3. Создать и откомпилировать в среде Keil μVision проект, реализующий обработку прерываний от двух источников – внешних входов прерываний EXT0 и EXT1. Отладить программу, выполняя ее пошаговое выполнение при различных установках регистров, настраивающих систему прерываний микроконтроллера. Исследовать фиксацию запросов прерываний по фронту и уровню, выбора прерываний на обработку в зависимости от приоритета, возможность вложения прерываний. Перечислить в отчете по лабораторной работе комбинации значений регистров IE, IP, TCON, обеспечивающие все возможные комбинации разрешений, приоритетов и фиксации запросов для прерываний EXT0 и EXT1.
1. Чем отличаются синхронные подпрограммы от асинхронных?
2. Какими командами может быть вызвана синхронная подпрограмма, какой командой она должна быть завершена?
3. Где сохраняется адрес возврата в вызывающую программу.
4. Что такое стек, по какому принципу он функционирует, где он
размещается в микроконтроллерах семейства Intel 8051, есть ли ограничения на его размер?
5. Перечислите способы, которыми передаются аргументы в процедуры.
6. Каким способом осуществляется возвращение результата выполнения процедуры?
7. Где распределяется память под локальные (внутренние) переменные подпрограмм?
8. Как сделать процедуру реэнтерабельной?
9. Что такое «соглашение о вызовах»?
10.Могут ли быть переданы аргументы в подпрограмму обработки прерывания? Может ли она возвратить результат?
11.В каком случае подпрограмма обработки прерывания может быть прервана для обработки другого прерывания?
12.Объясните отличия фиксации запроса прерывания по фронту от фиксации по уровню? Где устанавливается фиксация запросов
прерываний в микроконтроллерах семейства Intel 8051?
13.Что такое контекст задачи применительно к обработке прерываний? 14.Каким образом и где сохраняется контекст задачи при обработке