Программная модель процессора.

Под программной моделью, понимается набор внутренних регистров и флагов процессора, которые, тем или иным образом доступны программисту. Мы с Вами рассмотрим программную модель процессора 8086 (рис. 2.7), которая является базовой для всех микропроцессоров фирмы Intel, вплоть до процессора Pentium IV.

Как видно из рис. 2.7, процессор включает в свой состав несколько групп 16 разрядных регистров. Регистры ax, cx, dx и bx составляют группу регистров общего назначения (РОНы). Основной функцией этих регистров является временное хранение промежуточных результатов вычислений.

Как видно из рисунка 2.7, любой РОН можно рассматривать как один 16 разрядный регистр или как два 8 разрядных регистра. Например, bx – шестнадцатиразрядный регистр, bh и bl –восьмиразрядные регистры, причем bh соответствует старшему, а bl – младшему байтам bx.

В процессорах фирмы Intel на каждый из РОНов, помимо основной, возлагается еще и ряд дополнительных функций. Например, регистр cx используется в качестве счетчика в команде цикла loop. Мы здесь не будем рассматривать особенности каждого регистра, а будем вводить эти особенности по мере дальнейшего изложения материала. Подчеркнем только, что единственным регистром и группы РОНов, который может быть использован для адресации памяти, является регистр bx. Иначе говоря, невозможны конструкции, в которых ax, cx или dx,а также ah, al, bh, bl, ch, cl, dh или dl стоят в квадратных скобках. Транслятор на такие конструкции будет «ругаться», выдавая сообщения об ошибках.

Регистры sp, bp, si и di составляют группу регистров-указателей. Основное их назначение – участвовать в формировании Аэф в командах обращения к памяти («стоять в квадратных скобках»). Но если в какой-либо момент какие-либо из этих регистров для этих целей не нужны, можно использовать эти регистры (кроме sp!!!)в качестве РОНов. Регистры-указатели могут быть только 16 разрядными!

Следующую группу составляют сегментные регистры, с которыми мы уже познакомились в разделе 2.6.

Программная модель процессора. - student2.ru

Рис. 2.7

Особняком стоят два регистра. Это уже знакомый нам «указатель команд» ipи регистр флагов f. В последнем устанавливаются (чаще всего самим процессором) ряд флагов. Большинство флагов характеризуют результат последней операции, выполненной процессором. Состояние флагов, например, анализируют команды условных переходов, то есть флаги используются для организации ветвлений в программах. Формат регистра f приведен на рис. 2.8. Новичок в Ассемблере, как правило, вначале сталкивается только с одним флагом. Это флаг «нулевого результата» z (zero). Однако для полноты картины мы здесь все же приведем описание всех флагов.



15..12
резерв of df if tf sf zf - af - pf - cf

Рис. 2.8

cf (carry) – флаг переноса. Этот флаг устанавливается в единицу, если в процессе операции возникает перенос из старшего разряда (или был заем в старший разряд при вычитании). С этим флагом работают некоторые команды условных переходов. Программист может непосредственно влиять на значение этого флага, используя команды stc, clcи cmc(установить, сбросить, инвертировать).

pf(parity)– флаг четности. Устанавливается в единицу, если в младшем байте результата получилось четное число единиц. Флаг можно использовать для организации «контроля по четности», с ним работают некоторые команды условных переходов.

af (adjust) – флаг межтетрадного переноса. Устанавливается в единицу, если в процессе операции возникает перенос из 3-го бита в 4-й (или был заем из 4-го бита при вычитании). С флагом работают команды коррекции для двоично-десятичной (BCD) арифметики.

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

sf (sign)– флаг знака. Устанавливается в единицу, если в старшем разряде результата получилась единица, то есть результат является отрицательным числом. С флагом работают некоторые команды условных переходов.

tf (trap)– флаг трассировки. Если этот флаг установлен в единицу, программа выполняется в «пошаговом режиме», так как после каждой команды процессора автоматически генерирует прерывание типа 1. Таким образом, этот флаг используется для отладки. У программиста нет в распоряжении команд, влияющих на этот флаг. Поэтому изменить значение флага tfможно только косвенным образом, через стек.

if (interrupt enable)– флаг разрешения внешних прерываний. Если этот флаг установлен в ноль, процессор игнорирует запросы на прерывание, приходящие на вход INTR процессора, то есть запрещаются внешние прерывания (от клавиатуры, мыши и.т.д). Для работы с этим флагом есть две команды:

sti(set if)- if ß1–разрешить внешние прерывания;

cli(clear if) - if ß0–запретить внешние прерывания.

df (direction)– флаг направления, задает автоинкремент или автодекремент регистров si и di в строковых командах. Программист имеет возможность влиять на этот флаг с помощью команд stdи cld.

of (overflow)– флаг переполнения. Устанавливается в единицу, если в результате выполнения арифметической операции произошло переполнение разрядной сетки. С флагом работает команда into– «прерывание, если установлен флаг of» и некоторые команды условных переходов.

В заключение этого раздела отметим, что современные процессоры имеют 32 разрядный формат регистров и эти регистры обозначаются eax, ecx, …., edi.

Система команд.

Современные процессоры фирмы 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 (логическое И) производит поразрядное логическое умножение содержимого приемника на содержимое источника. Результат заносится в приемник. Например:

Источник: &
Приемник:
Результат:

Команда 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
Приемник:
Результат:

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

In al, 61h

Or al, 00000011b

Out 61h, al

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

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

Источник: =1
Приемник:
Результат:

Команда 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).Число, указанное в команде задает количество сдвигов (на сколько разрядов сдвигать)

Наши рекомендации