Дневники чайника. Чтива 0, виток0

Пятый день.
О словах и двойных словах
(форматы данных)

Не сказать чтоб эта тема была самая сложная, но то, что она самая запутанная - это 100%.

То, что я здесь напишу, довольно туго для восприятия, однако со временем всё уляжется. Не расстраивайтесь, если вдруг в этой главе покажется, что Ассемблер очень сложный, вспомните о таком фокусе.

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

Примерно такая же информация будет изложена в этой главе. На практике всё быстро усвоится, если вам придется сталкиваться с машинным кодом и данными.

Матрос, ты должен знать своего друга (со стороны потенциального противника) лучше, чем себя.

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

Байт - это минимальная расчетная единица бинарной армии. Запомнил?

2 байта организуются в слово (word).

4 байта организуются в двойное слово (dword, а пОлно - double word).

Dword - это самый распространённый набор битов в Win32-программах. Так как:

Dword =4 байта = 32 бита.

В предыдущей программе мы столкнулись вот с такой строкой:

00000003: 66C70701020304      mov  d,[bx],004030201

"d," здесь как раз и заменяет dword.

Я объяснил, что когда операнд находится в квадратных скобках, при команде mov это означает, что нужно производить действие по адресу в памяти, указанному операндом. То есть в BX раньше должен быть положен адрес. В ходе выполнения этой строки BX не изменяется, изменится только память по адресу, указанному BX. Размер изменяемой памяти dword (4 байта, двойное слово).

Остаётся маленький такой вопросик: почему в дизассемблере байты операндов команд процессора мы видим зеркально значениям операндов в командах Асма?

Вот:

          байты инструкции    Команда Ассемблера
00000003: 66C707 01020304     mov  d,[bx],004030201

Запомни, матрос, Бинарники очень хитрые. Для того, чтоб их враг путал байты по старшинству, в каждой целой боевой единице, будь то word (2 байта), dword (4 байта), qword (8 байт), байты строятся от младшего к старшему, черт их дери!

mov   dword ptr [bx],04030201

Число - 04030201h

Адрес, который был указан в BX, равен 0133h.

А действие выглядит так:

Адрес  значение
0133   01
0134   02
0135   03
0136   04

В отладчике вы видели это вот так:

0130  16 CD 20 01 02 03 04 24 E2 04 B7 9A 66 B9 FF FF
                ^  ^  ^  ^

Мы пишем и читаем текст по-европейски - слева направо. Но для чисел большинство людей использует арабскую запись - справа налево (хотя читаем числа тупо от старшей цифры =).

К великому огорчению, программистами был принят смешанный формат отображения данных. Каждый байт отображается по арабской системе, а целая группа байтов - по европейской. Выходит, что на экране мы видим разную запись. Если программа-дизассемблер или отладчик воспринимают группы байтов как ЦЕЛОЕ число, то оно отображается арабской записью, как в колонке команд Ассемблера: 04 03 02 01, а если речь идёт просто о нескольких байтах, то мы видим европейскую запись, только за букву принят целый байт, что и показано выше: 01 02 03 04. Всё это лишь вопрос отображения на экране или в документах. Например, если использовать запись цифровых значений от нижнего правого угла экрана до верхнего левого (справа налево, снизу вверх), то вообще ничего переворачивать не нужно! То есть если бы была принята запись "справа налево всё" или "слева направо всё", то подобных проблем не было бы вообще.



Допустим, мы набрали вот такую строку:

mov  word ptr [00000800h],0BBAAh

Здесь мы указали, что размер данных word (2 байта) и эти данные будут помещены в память по адресу 800h.

Объясню сейчас коротко.

Раз мы имеем заданный размер word (или как в Hiew'е "w,"), мы имеем некое ЦЕЛОЕ.

Младший байт (у нас AA) будет находиться по наименьшему адресу, а старший байт (BB) - по более старшему адресу.

Вот как эта строка будет выглядеть в Hiew'e:

Адрес     Байты              имя        операнды
00000000: C7060008AABB       mov      w,[0800],0BBAA

В колонке операндов - так, как мы вводили (число BBAAh). А вот в колонке байтов мы видим зеркальное расположение байтов операндов - 00 08, AA BB.
После выполнения такой команды в память байты запишутся вот так:
0800 AA (младший адрес - младший байт целого)
0801 BB (старший адрес - старший байт целого)

И точно так же устроены dword. Допустим:

mov  dword ptr [00000800h],0DDCCBBAAh
0800 AA (0-й адрес - 0-й байт целого)
0801 BB (1-й адрес - 1-й байт целого)
0802 CC (2-й адрес - 2-й байт целого)
0803 DD (3-й адрес - 3-й байт целого)

Вопрос, который наверняка возник у всех (и я предполагаю, что у многих в нецензурной форме): "На... в смысле зачем?"

Это нужно для того, чтобы процессор забирал байты из памяти, начиная с младшего (так быстрее вычислять). Ведь получается, что адрес числовой переменной любого размера, хоть word, хоть dword, будет указывать на младший байт в числе, далее пойдёт следующий байт в числе, и самый старший байт в числе всегда окажется в конце. Вычислять процессору так значительно быстрее.

Я думаю, что многие читатели совсем запутались, тут есть только одно утешение: перекладывать байты в голове совсем не нужно. Чтобы получить целое число из байтов или, наоборот, из числа сложить байты, достаточно переключить режим отображения в программе. Мало того, при простом программировании можно забыть об этих премудростях и вспоминать лишь изредка.

Всё, из теории остались только циклы и стек, о них мы будем говорить завтра.

Матрос! Я что-то не заметил, чтоб ты разрабатывал кнопку F10!

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

Послезавтра я увижу своё отражение в F10-key у тебя на клавиатуре или я высажу тебя на ближайшей заброшенной планете.

А чтоб было веселей давить на кнопку F10, загони в отладчик следующую программу (prax03.com).

Набивайте всё сами, только так можно научиться.

В Hiew'e она должна выглядеть так:

00000000: B80300     mov  ax,00003
00000003: CD10       int  010
00000005: B402       mov  ah,002
00000007: 8B167501   mov  dx,[0175]
0000000B: CD10       int  010
0000000D: FEC6       inc  dh
0000000F: 80C203     add  dl,003
00000012: 89167501   mov  [0175],dx
00000016: B409       mov  ah,009
00000018: BA5001     mov  dx,00150
0000001B: CD21       int  021
0000001D: 803E760119 cmp  b,[0176],019
00000022: 75E1       jne  000000005
00000024: B410       mov  ah,010
00000026: CD16       int  016
00000028: CD20       int  020

Но это не всё, теперь переключитесь на Hex-режим (F4) и добейте программу следующими байтами после всего кода. Это будут "данные".

00000020:                             20 20 20-20 20 20 20
00000030:  20 20 20 20-20 20 20 20-20 20 20 20-20 20 20 20
00000040:  91 E2 E0 AE-AA A0 20 E2-A5 AA E1 E2-A0 3A 20 20  Строка текста:
00000050:  2D 3D 80 E1-AC 3D 2D 24-20 20 20 20-20 20 20 20  -=Асм=-$
00000060:  20 20 20 20-20 20 20 20-20 20 20 20-20 20 20 20
00000070:  77 6F 72 64-21 00 00 21-77 6F 72 64-20 20 20 20  word!  !word
00000080:  20 20 20 20-20 20 20 20-20 20 20 20-20 20 20 20

На самом деле все эти пробелы (20h) программе НЕ нужны. Но когда вы будете смотреть программу в отладчике, они вам помогут.

Если у вас будет сдвиг хоть на байт, программа будет ошибочной. Поэтому проверьте, чтобы строка "-=Асм=-$" начиналась с 50h и, что ещё более важно, два нулевых байта (00 00) должны быть в файле по адресам 75 и 76h. Обязательно посмотрите в отладчике, что будет происходить с этими байтами (там они будут 175h и 176h). Всё остальное здесь мишура и для выполнения программы совершенно не имеет значения.

При отладке могут появляться сообщения насчёт экрана... ну и фиг с ними. Если в CV опция Screen Swap ещё не выключена, то это обязательно нужно сделать.

Результат действия этой программы - вывод строк по диагонали от верхнего левого угла до нижнего правого угла.

Здесь очень много нового, и я надеюсь, вам будет интересно узнать, как работают новые команды CMP и JNE.

Попробуйте сами разобраться, что происходит в программе на практике. Как я уже писал, прерываний (команда int) при написании программ Win32 мы использовать не будем. Поэтому можете не заострять на них внимание. Достаточно знать, что это полезные подпрограммы, часть которых заложена ещё в BIOS (basic input/output system - базовая система ввода/вывода).

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

В данном примере будут задействованы int 10h для очистки экрана (AL=3) и для расположения курсора текста (AH=2). Ну и int 21h для вывода текста на экран. Всё, других прерываний в уроках больше не будет. О них за долгие годы написано достаточно.

Вот как программа должна трактоваться (сразу скажу - про метки я всё объясню позже).

mov    ax,03      ; В AX значение 3 (параметр видеорежима 80x25)
int    010        ; Подпрограмма установит текстовый видеорежим 80х25,
                  ; при этом экран ДОС очистится

TuLuLa:           ; Всего лишь условная метка, в коде программы её нет 
                  ; Здесь могли быть любые буквы и ":"

mov    ah,02      ; В AH поместить 02 (для int10 - установка курсора)
mov    dx,[0175h] ; В DX загрузить значение из памяти по адресу 175h
                  ; в первый раз там нули, 
                  ; а в следующий проход значения будут увеличены

int    10         ; Установит текстовый курсор в положение, указанное в DX
                  ; DH - номер линии, DL - номер колонки (верх.лев.- 0000)

inc    dh         ; Прибавить 1 к значению DH
add    dl,03      ; Прибавить 3 к значению DL

mov    [0175h],dx ; Сохранить DH и DL в память по адресу 175h

mov    ah,09      ; Функция в int21h - вывод на экран с позиции курсора
mov    dx,00150h  ; В DX адрес текстовой строки, заканчивающейся $
int    021        ; Подпрограмма выведет текстовую строку на экран


cmp    byte ptr [176h],19h ;Сверяется значение байта в памяти с числом 25d
jne    TuLuLa              ;и если значения не равны,
                           ;то прыг на выполнение от метки TuLuLa

mov    ah,010     ; а если были равны, то выполнится эта строка
int    016        ; Подпрограмма дождётся нажатия клавиши
                  ; и вернёт управление на следующую строку

int    020        ; код завершения программы

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

Bitfry

<<предыдущая глава     следующая глава>>

Вернуться на главную

Hosted by uCoz