Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лаба 2 / КНИГА_АСМ.docx
Скачиваний:
1
Добавлен:
09.02.2024
Размер:
160.52 Кб
Скачать
    1. Система команд.

Современные процессоры фирмы Intel имеют развитую систему машинных команд. Выпущенная фирмой книга с описанием всех ассемблерных команд имеет объем более 1000 листов. Однако это не должно нас пугать, поскольку начинающему программисту в его программах понадобиться от силы 10 – 15 машинных команд. Кроме того, ни один программист никогда не помнит все эти машинные команды наизусть. Опытный программист просто помнит «что такая команда есть» и, когда она ему понадобиться, обращается к справочнику. Мы в этом разделе остановимся только на командах, с которыми сразу столкнется в своей работе новичок.

  • Команда MOV приемник, источник.

Команда передает содержимое источника в приемник. В качестве источника могут выступать регистр, ячейка памяти и непосредственный операнд (передается число, непосредственно заданное в команде). Приемником могут быть регистр или ячейка памяти. Например:

mov cx, 0b800h

; Команда загружает в cх число b800h

mov al, ah

; Эта команда переписывает содержимое аh в al.

mov perem, si

; Эта команда загружает в переменную, которую ; программист назвал perem, содержимое регистра si.

mov bp, [bx+4]

;Эта команда загружает в регистр bp слово из ячейки ; памяти с адресом Аф = (ds)*16 + (bx) + 4.

Важным является следующий момент: в качестве источника и приемника в одной команде не могут одновременно выступать две ячейки памяти!! То есть команда mov perem, [bx + 4] заставит транслятор сформировать сообщение об ошибке. Правильно надо было писать, например, так:

mov ax, [bx + 4]

mov perem, ax.

Отметим также, что все, что сказано выше об источнике и приемнике, справедливо и для всех остальных команд процессора.

Приведем еще один пример:

mov [si], 7 ; команда заносит в память по адресу Аф = (ds)*16 + (si) число 7. Синтаксически команда написана правильно, а транслятор выдает предупреждение: Argument needs type override. А дело заключается в том, что транслятор не может по такой записи понять, что надо передавать в память байт или слово? Соответственно он может сформировать неверный код операции. О каком формате числа идет речь в такой команде, транслятору должен сообщить программист, написав:

mov byte ptr [si], 7 ; (указатель на байт) речь идет о байте.

mov word ptr [si], 7 ; речь идет о слове.

  • Команды INC приемник и DEC приемник.

Команда inc (инкремент) прибавляет единицу к содержимому приемника. Команда dec (декремент) вычитает единицу из содержимого приемника. Например:

inc cl ;содержимое регистра cl увеличивается на единицу

dec di ;содержимое регистра di уменьшается на единицу

Inc word ptr [bx]

dec perem.

  • Команды ADD приемник, источник и SUB приемник, источник.

Команда add прибавляет содержимое источника к содержимому приемника, результат заносится в приемник. Команда sub вычитает содержимое источника из содержимого приемника, результат заносится в приемник. Например:

add ah, 32 ; прибавить 32 к содержимому регистра ah

sub dl, ch ; вычесть содержимое ch из содержимого dl (результат в dl)

sub perem, bx

add ax, [di]

add byte ptr [bx + si + 2], 3.

  • Команда CMP приемник, источник

Команда cmp (сравнение) вычитает содержимое источника из содержимого приемника, но, в отличие от команды sub, результат вычитания никуда не заносится. Результатом работы команды cmp является установка соответствующих флагов в регистре флагов. Команда cmp всегда используется в паре с одной из команд «условного перехода» (je метка – «перейти, если равно», jne метка – «перейти, если не равно» и другими). Например:

cmp al, 0

je m1

cmp ax, bx

jne not_equal

cmp byte ptr [si – 14], 0ffh

je exit

  • Команда безусловного перехода JMP метка

Команда осуществляет безусловный переход на указанную метку. Вместо метки транслятор впоследствии (при трансляции) подставит необходимое смещение (число). В качестве метки можно использовать любое выражение, начинающееся не с цифры. Чтобы транслятор понял, что это метка, после метки ставится двоеточие (не в команде!!). Например:

_m1: cmp ah, 3 ; в ah тройка?

jne _m2 ; если нет, прыгаем на _m2

.

.

.

jmp _m1 ; прыгаем на _m1

_m2:

add bx, 32

  • Команды условных переходов.

je метка – переход, если равно

jz метка – переход, если результат равен нулю (флаг zf установлен в единицу). Собственно это другая запись команды je.

jne метка – переход, если не равно (эквивалентная команда jnz).

ja метка – переход, если больше

jae метка – переход, если больше или равно

jb метка – переход, если меньше

jbe метка – переход если меньше или равно

Например:

sub ax, 40 ; вычитаем из ax 40

jnz m17 ; если результат не равен нулю, прыгаем на m17

cmp al, bh

jae povtor ;если содержимое al больше или равно содержимому bh, прыгаем ; на povtor

Достаточно часто приходится сталкиваться со случаем, когда на синтаксически правильной команде условного перехода транслятор выдает ошибку: Relative jump out of range. Связано это с тем, что команда условного перехода может обеспечить прыжок только на плюс/минус 128 байт, то есть приблизительно на 30 – 40 команд (вперед или назад по программе). А если надо прыгнуть на большее расстояние? Как с этим бороться? Стандартный способ борьбы состоит в использовании команды безусловного перехода (jmp), обеспечивающей прыжок на плюс/минус 64 Килобайта. Например, рассмотрим фрагмент программы:

cmp ax, 0 ; в ax ноль?

je m100 ; если да, прыгаем на m100, если нет, идем на следующую команду

mov bx, 40

На команде je m100 транслятор выдает вышеуказанную ошибку. Перепишем этот фрагмент:

cmp ax, 0 ; в ax ноль?

jne m200 ; если нет, прыгаем на m200, если да, идем на следующую команду

jmp m100 ; прыгаем на m100

m200: mov bx, 40

Логика программы не изменилась, а вот ошибки больше не будет.

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

  • Команда LOOP метка

Команда loop (цикл) вычитает единицу из содержимого регистра cx и, если в результате получился «не ноль», переходит на указанную метку. В качестве примера рассмотрим следующий фрагмент:

mov dh, 0

mov cx, 11 ; число повторений цикла

m1:

inc dh

loop m1

mov al, dh

Данный фрагмент выполняется следующим образом: сначала в dh загружается 0. Затем в цикле к dh 11 раз прибавляется единица. В результате этого фрагмента мы будем иметь: cx = 0, dh =11, al =11. Конечно тех же результатов можно было бы достичь проще:

mov cx, 0

mov dh, 11

mov al, dh

но здесь нет цикла.

Распространенной ошибкой, приводящей к самым плачевным последствиям, является написание бесконечного цикла. Например, следующий фрагмент приведет к зависанию программы:

mov dh, 0

m1:

mov cx, 11

inc dh

loop m1

В cx занесется 11, команда loop вычтет из cx единицу, получится «не ноль», произойдет переход на метку m1, в cx снова занесется 11 и так до бесконечности. Просто метка m1 поставлена не там, где нужно (правильный вариант смотри выше).

Еще одной менее очевидной, но не менее неприятной по последствиям, ошибкой является занесение внутри цикла (по забывчивости программиста) в регистр cx новой информации, которая портит текущее значение счетчика цикла. Если же изменение cx внутри цикла нам «жизненно необходимо», то надо предварительно запомнить текущее содержимое cx (например, в стеке командой push cx), а затем восстановит это содержимое (pop cx) перед выполнением команды loop.

  • Команды IN al, адрес порта и OUT адрес порта, al

Команда in передает байт из заданного в команде порта в регистр al. Команда out передает байт из регистра al в заданный в команде порт. В качестве адреса порта может выступать любое число, лежащее в диапазоне 0 – 255 (0 –ffh). Порт – это регистр, которому в системе присвоен адрес. Например, контроллер клавиатуры имеет 2 порта с адресами 20h и 21h, таймер – 4 порта с адресами 40h, 41h, 42h и 43h и.т.д. Приведем примеры команд:

in al, 60h ; читаем скэн-код нажатой клавиши из порта клавиатуры

out 40h, al ; заносим байт коэффициента пересчета в 0-й канал таймера

Обратите внимание, что обмен информацией с портами ведется только через регистр al (это не совсем правильно, поскольку имеются и другие варианты команд in и out, но для начинающего программиста проще использовать только рассмотренные выше команды).

  • Команда AND приемник, источник

Команда and (логическое И) производит поразрядное логическое умножение содержимого приемника на содержимое источника. Результат заносится в приемник. Например:

Источник:

&

10011101

Приемник:

01111010

Результат:

00011000

Команда and часто используется, когда надо сбросить в ноль конкретный бит (биты) в байте или слове, не меняя значение других бит этого байта (слова). Приведем пример:

in al, 61h ; читаем 61 порт

and al, 11111100b ; обнуляем два младших бита

out 61h, al ; записываем обратно в 61 порт Эти три команды запрещают звучание встроенного динамика (спикера). Сначала мы считываем содержимое порта 61h в регистр al. Затем обнуляем два младших бита al (запрещаем звук). При этом все остальные биты мы оставляем в их исходном состоянии, чтобы ненароком не нарушить работу системы. После этого отправляем измененную информацию обратно в порт 61h.

Команда and также часто используется, когда надо проверить значение конкретного бита в байте или слове. Например, надо проверить установлен ли 1-й бит регистра al в единицу. Эту проверку можно организовать так:

and al, 00000010b

jnz m99 Если в 1-м бите стоял 0, в результате выполнения первой команды получится ноль. Вторая команда совершает прыжок на m99, если результатом первой команды был «не ноль», то есть если 1-й бит был установлен в единицу. Недостаток такой проверки – после нее содержимое al будет испорчено.

  • Команда OR приемник, источник

Команда or (логическое ИЛИ) производит поразрядное логическое сложение содержимого источника и содержимого приемника. Результат заносится в приемник. Например:

Источник:

V

10011101

Приемник:

01111000

Результат:

11111101

Команда or часто используется, когда надо установить в единицу конкретный бит (биты) в байте или слове, не меняя значение других бит этого байта (слова). В качестве примера приведем последовательность команд, разрешающих звучание встроенного динамика:

in al, 61h

or al, 00000011b

out 61h, al

  • Команда XOR приемник, источник

Команда xor (исключающее ИЛИ) производит поразрядное сложение по модулю 2 содержимого приемника и содержимого источника. Результат заносится в приемник. Например:

Источник:

=1

10011101

Приемник:

01111000

Результат:

11100101

Команда xor часто используется, если надо инвертировать значение какого-либо бита (битов) в байте или слове. Например:

xor al, 11100000b Команда инвертирует значение трех старших битов в регистре al.

Кроме того, команду xor удобно использовать для обнуления содержимого любого регистра:

xor ax, ax ; после этого в ax будет 0.

  • Команда LEA регистр, имя переменной

Команда загружает в указанный в команде регистр эффективный адрес указанной в команде переменной (то есть смещение этой переменной относительно начала сегмента). Например:

lea bx, perem ; после этого в bx адрес perem Команда lea имеет ассемблерный эквивалент. Приведем команду, эквивалентную рассмотренной в примере:

mov bx, offset perem ; после этого в bx адрес perem Мы в дальнейшем будем использовать как раз второй вариант записи. Он ничем не лучше первого, но более нагляден (по нашему мнению).

  • Команда DIV регистр

Команда div (деление) делит содержимое регистра ax на содержимое указанного в команде 8 разрядного регистра. Результат возвращается в al (частное) и в ah (остаток). Если в команде div указан 16 разрядный регистр, то на его содержимое делится не ax, а регистровая пара dx:ax (старшие 2 байта в dx). Соответственно частное возвращается в ax, а остаток в dx. Например:

div cl ; ax / cl , частное в al, остаток в ah div bx ; dx:ax / bx, частное в ax, остаток в dx

Мы будем использовать эту команду, например, для перевода чисел из двоичной системы в десятичную.

Команда div таит в себе одну опасность. Если частное не помещается в отведенный для него регистр, происходит прерывание «Divided overflow». Например, если Ваша программа содержит такой фрагмент:

mov ax,1000

mov cl, 1

div cl

программа выполняться не будет, зато на экране появиться надпись «Divided overflow». Связано это с тем, что при делении 1000 на 1 частное, равное 1000, не может поместиться в 8 разрядный регистр al, поскольку в al можно поместить максимум 255.

  • Команда INT число

Команда int n – программное прерывание (n – число, лежащее в диапазоне 0-255 или 0-ffh). С помощью таких команд программист вызывает сервисные подпрограммы DOS и BIOS, выполняющие (за программиста) массу полезной работы. Эти подпрограммы принимают информацию с клавиатуры, выводят информацию на экран, работают с дисками, распределяют память и.т.д. Параметры, передаваемые в подпрограмму, задаются перед вызовом int n в заранее оговоренных регистрах. Кроме того, поскольку одна и та же подпрограмма, задаваемая числом n в команде int, зачастую выполняет целый набор различных сервисных функций, номер конкретной запрашиваемой функции задается перед вызовом в регистре ah. Если такая подпрограмма возвращает результаты, то они возвращаются в заранее оговоренных регистрах. Например:

mov ah, 0eh

mov al, ‘A’

int 10h

Это прерывание BIOS c номером 10h, функция 0eh. Это прерывание выводит на экран в текущую позицию курсора символ, ASCII код которого задан в регистре al. В нашем случае на экран выведется буква А. Никаких результатов в этом случае подпрограмма не возвращает. Второй пример:

mov ah, 7

int 21h

Это 21-е (DOS) прерывание, функция 7. Программа в этом месте останавливается и ждет нажатия клавиши на клавиатуре. После того, как клавиша нажата, ее код возвращается в регистре al.

Основные ошибки, которые допускает программист при использовании команд int:

  • забыл поставить букву h в номере прерывания, в результате это оказалось совсем другое прерывание, выполняющее совсем другие функции;

  • входные параметры заданы либо неправильно, либо не в тех регистрах.

    • Команды PUSH регистр и POP регистр

Команда push заталкивает в стек содержимое регистра, а команда pop выталкивает в регистр информацию из вершины стека. Еще раз подчеркнем, что в этих командах недопустимы 8 разрядные регистры.

  • Команды SHR регистр, число и SHL регистр, число

Эти команды сдвигают содержимое указанного регистра соответственно вправо (shr) и влево (shl). Число, указанное в команде задает количество сдвигов (на сколько разрядов сдвигать)

Соседние файлы в папке Лаба 2