Assembler / P10
.pdf10. Кодирование команд.
Мы не будем детально изучать кодирование всех команд: лучше предоставить Ассемблеру работу по переводу мнемоники команд в их код. Однако полезно иметь ясное представление о принципах кодирования команд. Заметим, что мы уже изучили кодирование команд перехода.
10.1. Однооперандные команды.
Для примера возьмем команду inc dst — она увеличивает операнд на единицу. Имеется несколько вариантов кодирования этой команды.
10.1.1. Инкремент 16-разрядного регистра. Код команды занимает один байт:
7 6 5 4 3 2 1 0
КОП |
|
|
reg16 |
|
Здесь КОП — код операции, для inc |
reg16 это 01000. 16-разрядные регистры ко- |
|||
дируются в соответствии с табл. 10.1. |
|
|
|
|
|
Таблица 10.1. |
|||
|
000 |
|
AX |
|
|
001 |
|
CX |
|
|
010 |
|
DX |
|
|
011 |
|
BX |
|
|
100 |
|
SP |
|
|
101 |
|
BP |
|
|
110 |
|
SI |
|
|
111 |
|
DI |
|
Тогда inc cx кодируется так: 01000 001= 41h. Упражнение. Проверить это утверждение в TD.
10.1.2. Инкремент любого регистра (8- или 16-разрядного), байта или слова памяти. Код команды занимает два байта (старший байт носит название постбайта):
постбайт
КОП |
w |
mod КОП r/m |
КОП теперь содержится в двух полях: в битах 7:1 первого байта и 5:3 постбайта. Для команды inc содержимое этих полей 1111111 и 000.
w (word) — длина операнда. Если w = 1 (ДА) — операнд-слово, w = 0 (НЕТ) — опе- ранд-байт.
mod (mode) — режим. При mod = 11 операнд находится в регистре, при mod = 00, 01, 10 операнд располагается в памяти.
r/m (register/memory) — если операнд находится в регистре, это поле содержит номер регистра.
10.1.2.1. Операнд в регистре.
Таблицу кодирования регистров придется расширить (табл. 10.2).
Таблица 10.2.
r/m |
w=1 |
w=0 |
000 |
AX |
AL |
|
|
|
|
|
|
001 |
|
|
|
CX |
|
|
|
CL |
|
|
|
|
|
|
|
|
|
|
|
|
010 |
|
|
|
DX |
|
|
|
DL |
|
|
|
|
|
|
|
|
|
|
|
|
011 |
|
|
|
BX |
|
|
|
BL |
|
|
|
|
|
|
|
|
|
|
|
|
100 |
|
|
|
SP |
|
|
|
AH |
|
|
|
|
|
|
|
|
|
|
|
|
101 |
|
|
|
BP |
|
|
|
CH |
|
|
|
|
|
|
|
|
|
|
|
|
110 |
|
|
|
SI |
|
|
|
DH |
|
|
|
|
|
|
|
|
|
|
|
|
111 |
|
|
|
DI |
|
|
|
BH |
|
|
|
|
|
|
Пример: Расшифруем код команды inc cl — FE C1 |
|
|
|
|
||||||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
1 |
1 |
1 |
1 |
1 |
|
1 |
|
0 |
|
1 |
|
1 |
0 |
0 |
0 |
0 |
0 |
1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
CL |
|
||||||
|
|
|
КОП |
|
|
|
|
w |
reg8 |
КОП |
|
|
На первый взгляд парадокс! Код inc cl занимает два байта, а код inc cx — один байт! Конечно, inc cx можно было закодировать и в двух байтах (какой бит для этого надо изменить?), но для инкремента 16-разрядных регистров предусмотрен укороченный формат кода команды. Именно его и выбирает Ассемблер.
10.1.2.2. Операнд в памяти.
Содержимое поля mod расшифровывается так: 00 — смещения нет;
01 — D8 (расширяется со знаком до D16); 10 — D16.
Здесь D8 — число, занимающее байт, D16 — число, занимающее слово (D — сокращение от слова DATA — данные).
Содержимое поля r/m теперь означает комбинацию базового и индексного регистров
(табл. 10.3).
Таблица 10.3.
|
|
|
|
|
|
|
|
|
|
|
|
000 |
|
BX+SI |
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
001 |
|
BX+DI |
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
010 |
|
BP+SI |
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
011 |
|
BP+DI |
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
100 |
|
|
SI |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
|
|
DI |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
110 |
|
|
D16 |
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
111 |
|
|
BX |
|
|
|
|
|
|
|
|
|
|
|
|
|
Исключение: mod = 00, r/m = 110 — это не [BP]! |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||||||
Пример. Дешифруем команду FF445C. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
|
0 |
1 |
|
0 |
0 |
0 |
1 |
0 |
0 |
|
0 |
1 |
0 |
1 |
1 |
1 |
0 |
0 |
КОП 1111111 000 — команда inc; w = 1 — операнд-слово; mod = 01 — имеется 8- разрядное смещение, D8 = 5C, расширяем знак: 005С; r/m = 100 — соответствует индексному регистру SI.
Итак, мнемоника команды inc word ptr [si+5C].
Теперь посмотрим, как будет работать эта команда. Для этого сделаем допущения:
SI = 0A086h, DS = 20F0h. Тогда эффективный адрес равен 0A086h + 005Ch = 0A0E2h —
смещение в сегменте. Так как в адресаци не участвует BP, то сегментный адрес выбирается из DS. Физический адрес: 20F00h + 0A0E2h = 2AFE2h. Операнд — слово — расположен в байтах с адресами 2AFE2 и 2AFE3.
Предположим, что до операции [2AFE2] = 0FFh, [2AFE3] = 00h. Тогда после операции
[2AFE2] = 00h, [2AFE3] = 01h.
10.1.3. Прямая адресация.
В команде непосредственно указывается адрес (точнее, смещение) операнда, например inc word ptr [200]. Разработчики процессора хотели закодировать прямую адресацию какой-либо комбинацией mod и r/m, но все сочетания оказались задействованы для регистровой и косвенной адресации. Пришлось привлечь редко используемую комбинацию mod = 00 и r/m = 110 (т.е. [BP]; далее мы увидим, что использовать этот метод адресации нет необходимости).
Смещение кодируется в двух байтах в самой команде:
|
постбайт |
|
|
||
00 |
110 |
|
|
|
|
|
|
|
DISP-LO (младший |
|
DISP-HI (старший |
|
|
|
байт смещения) |
|
байт смещения) |
Упражнение. Вручную закодируйте inc word ptr [200] и проверьте результат в отладчике.
Упражнение. Выясните, как мини-ассемблер отладчика закодирует команду inc word ptr [bp].
Составим сводную таблицу (табл. 10.4) методов адресации в зависимости от содержимого полей r/m, mod и w. Эта таблица не содержит для нас новой информации.
Таблица 10.4
|
|
|
Поле mod |
|
|
|
|
Поле r/m |
00 |
01 |
|
10 |
|
11 |
|
|
|
w=0 |
|
w=1 |
|||
|
|
|
|
|
|
||
000 |
BX+SI |
BX+SI+D8 |
|
BX+SI+D16 |
AL |
|
AX |
001 |
BX+DI |
BX+DI+D8 |
|
BX+DI+D16 |
CL |
|
CX |
010 |
BP+SI |
BP+SI+D8 |
|
BP+SI+D16 |
DL |
|
DX |
011 |
BP+DI |
BP+DI+D8 |
|
BP+DI+D16 |
BL |
|
BX |
100 |
SI |
SI+D8 |
|
SI+D16 |
AH |
|
SP |
101 |
DI |
DI+D8 |
|
DI+D16 |
CH |
|
BP |
110 |
D16 |
BP+D8 |
|
BP+D16 |
DH |
|
SI |
111 |
BX |
BX+D8 |
|
BX+D16 |
BH |
|
DI |
Еще раз заметим, что красоту этой таблицы портит прямая адресация при r/m = 110, mod = 00.
Может возникнуть вопрос: мы узнали, как кодируется регистровая, прямая и косвенная адресации. А как же непосредственная? Ответ: в однооперандных командах непосредственного операнда не бывает (команда не может изменить свой код — ведь он находится в очереди команд в центральном процессоре).
10.2. Двухоперандные команды.
На этот раз в качестве примера рассмотрим команду сложения add dst,src. Поставим себя на место разработчиков системы команд процессора. Если приемник и
источник оба находятся в памяти, то для кодирования каждого из них потребуются свои поля mod и r/m. Сосчитаем количество битов, необходимых для кодирования: на поле mod нужно 2 + 2 бита и на поле r/m — 3 + 3 бита — всего 10 битов. Получается, что постбайта не хватит, и ради двух битов придется в код команды включить дополнительный байт. Чтобы сократить длину команд, разработчики решили, что хотя бы один операнд должен обязательно находиться в регистре (мы пока не рассматриваем случай непосредственного операнда). Тогда для одного операнда потребуется поле reg — 3 бита, а для другого —
mod и r/m — 2 + 3 = 5 битов. Всего 8 бит — ровно байт. Формат типичной команды имеет вид:
|
|
|
|
|
|
|
|
|
|
|
постбайт |
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
КОП |
|
|
d w |
|
mod reg |
r/m |
Для команды ADD, например, код операции 0000000. Назначение поля w уже известно. Новое поле d (destination — приемник) несет следующую информацию: если d = 0, то приемник определяется полями mod и r/m (т.е. этот операнд находится в памяти или регистре), если d = 1, то приемник определяется полем reg (т.е. заведомо находится в реги-
стре). |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Пример. Дешифруем код 02 EB. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||
|
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
|
1 |
1 |
|
1 |
|
0 |
|
1 |
0 |
|
1 |
|
1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
КОП |
|
|
d |
w |
|
mod |
|
|
reg |
|
|
r/m |
|
|
||||||
w = 0 — операнды-байты; d = 1 |
— приемник |
в |
регистре, определяемом полем reg; |
reg = 101 — это регистр CH (в соответствии с табл. 10.4); mod = 11 — источник также в регистре; r/m = 11 — это регистр BL (по табл. 10.4). Итак, код соответствует команде add ch,bl.
Упражнение. Реассемблировать вручную код 00 00. Проверить результат в отладчике.
10.3. Непосредственный операнд.
Напомним, что удобство использования непосредственного операнда определяется двумя причинами:
1)уменьшается требуемая память (иначе пришлось бы в команде хранить адрес константы, а саму константу хранить в памяти);
2)непосредственный операнд берется прямо из кода команды, поэтому не тратится время на обращение к памяти для выборки операнда.
Опять обратимся к примеру. Команда сложения с непосредственным операндом add dst, imm максимально кодируется шестью байтами.
постбайт |
|
|
|
|
|
DISP-LO |
DISP-HI |
DATA-LO |
DATA-HI |
|
присутствие зависит от |
|
имеется, |
|
|
поля mod |
|
если s:w=01 |
Байты, которые необязательно присутствуют в коде команды, показаны пунктиром. Сначала посмотрим структуру первых двух байтов:
1 |
0 |
0 |
0 |
0 |
0 |
|
|
|
|
|
0 |
0 |
0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
КОП |
|
|
s w |
|
mod КОП r/m |
Сразу заметим, что код операции ADD изменился, увеличилась и его длина: вместо 6 бит стало 9. Бит приемника d исчез — понятно почему, ведь непосредственный операнд не может быть приемником. Поля mod и r/m нам уже знакомы. Соответственно, в зависимости от mod в коде может присутствовать смещение D8 или D16.
Зато появился новый бит s (sign — знак). Он занимает место d, т.к. бит d теперь не нужен. Смысл бита s таков. В арифметических операциях часто используются небольшие по величине непосредственные операнды и отводить для них целое слово слишком расточительно. При w = 0 (операнд-байт) значение s не играет роли. При w = 1 (операнд-слово): если s=0, то в команде присутствуют все 16 бит DATA; если же s = 1, то присутствуют 8 бит (DATA-LO), которые расширяются со знаком до 16-разрядного операнда. Благодаря биту s экономится один байт! Это — кодировка с расширением знака (sign-extended encodings).
Упражнение. Введите в TD в панели данных окна CPU по адресу ds:0100 (этот адрес совпадает с cs:0100 при входе в TD) последовательность байтов: 81,0C3,0FF,0,83,0C3,0FF. Объяснить результат, полученный в панели кода.
Казалось бы, мы полностью разобрались с командой ADD. Но есть еще один ее вариант! При выполнении команды сложения приемником, как правило, является аккумулятор (AX или AL). Для прибавления к аккумулятору введена специальная кодировка (укоро-
ченный формат ADD): |
|
|
|
||
|
0 0 0 0 0 1 0 |
w |
|
DATA-LO |
DATA-HI |
|
|
|
|
|
присутствует, если w=1 |
10.4. Общие замечания о кодировании команд.
Подведем итоги. Создатели микропроцессора 8086 приложили максимум усилий для компактного кодирования команд: для часто используемых вариантов команд имеются сокращенные форматы. В качестве одного из операндов полезно по возможности использовать аккумулятор, т.к. для него нередко генерируется меньшая по длине команда.
Очень много форматов используется для кодирования наиболее распространенной команды mov. Поэтому в примерах эта команда отсутствовала.
Длина команды может составлять от одного до шести байт (для процессора 8086). Полученные сведения могут показаться излишними, но теперь вы должны четко по-
нимать, например, почему нельзя использовать адресацию [CX]. Потому что разработчикам не хватило номеров для методов адресации. Стал ясным запрет на использование в команде двух операндов из памяти.
Еще одно упражнение, которое поможет понять нехитрые программистские трюки для сокращения длины кода программы.
Упражнение. 1) При обработке массива слов для перехода к следующему слову нужно увеличивать на два содержимое индексного регистра. Что короче: две команды inc si или одна команда add si,2 ?
2) Какая команда обнуления регистра короче: mov bx,0 или xor bx,bx ?
В процессоре 80386 набор методов адресации существенно увеличен. Естественно, увеличилась и возможная длина кода команды. Набор методов адресации 8086 входит в 80386 как подмножество.
Задание A5. Получить с помощью отладчика коды команд, выделить в этих кодах поля и расшифровать их.
1) dec ax; 2) dec al; 3) inc word ptr [bx+si+24h]; 4) inc byte ptr [bx+di–4A0h]; 5) sub [bx+si],ah; 6) sub word ptr [bx+14h],3; 7) sub byte ptr [bx+14h],–3.
10.5. Префикс замены сегмента.
С помощью изученных методов адресации можно обращаться только к сегменту данных, адрес которого хранится в DS, или к стековому сегменту, адрес которого хранится в SS. А если нужно обратиться к данным, хранящимся в сегменте, адрес которого лежит в ES? Тогда перед кодом команды нужно вставить байт — префикс замены сегмента (segment override prefix). Формат этого байта:
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
0 |
0 |
1 |
seg |
|
1 |
1 |
0 |
Здесь seg — номер сегментного регистра, кодируемый в соответствии с таблицей 10.5.
Таблица 10.5.
ES |
00 |
CS |
01 |
SS |
10 |
DS |
11 |
Может возникнуть вопрос: а к какому из операндов команды относится префикс замены сегмента. Ответ прост: в команде только один операнд может ссылаться на ячейку памяти, поэтому неоднозначности не возникает.
Пример. Для команды mov ax, es:[bx], или mov ax, es:[bx] (эти две формы эквивалентны) Ассемблер сгенерирует перед кодом команды префиксный байт 26h=00100110. Проверьте.
10.6. Команда загрузки исполнительного адреса.
С косвенной адресацией тесно связана команда загрузки адреса.
Загрузить исполнительный адрес |
lea r16,mem |
reg ← адрес mem |
Load Effective Address |
|
флаги не изменяются |
lea r16,m |
|
|
Врегистр загружается не сам операнд, а его адрес, точнее, смещение в текущем сегменте данных.
Пример. mov si,200 mov bx,10
lea di,[bx+si+4]
Врезультате выполнения этих команд DI = 214.
Упражнение. Результат выполнения команды lea di,[200]?