TAJGA FASM Tutorial

Все это определяет метки, означающие адреса данных, определяемых следом за этими директивами.

Синтаксис:Более простой задать метку — просто написать ее имя и за ним двоеточие "

:

"Но пока мы не будем использовать эту форму.

5.2. Определение переменных

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

Взгляните на это:

Эта программа, скорее всего, вызовет сбой, так как после того, как процессор выполнит mov al,10 он наткнется на строку. Но в программе в машинных кодах нет разницы между строками и инструкциями. Все они компилируются в массив цифровых значений (байты). И для процессора нет способа различить, что представляют собой эти байты — строку или инструкцию. В данном примере, процессор начнет выполнять инструкции, числовые значение которых (в машинном коде) соответствуют ASCII-значениям символов строки 'this is a string'.

Теперь взгляните на это:

Эта программа будет работать нормально, так как перед тем как процессор достигнет байтов строки, выполнится инструкция int 20h, которая завершит выполнение программы. Так что байты строки не будут выполнены, а просто будут занимать какое-то место в программе. Вот так и можно определять переменные — задать какие-то данные в том месте, где процессор не будет пытаться их выполнять (после

int 20h

, в нашем варианте).

Итак, программа, где есть переменная размером один байт и значением 105:

Последняя строка определяет переменную, содержащую 105.

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

Сейчас мы уже знаем адрес переменной. Он представлен меткой my_first_variable.

Как добраться до содержимого переменной? Возможно, Вы думаете, к примеру так:

но нет! Вспомните, я говорил, что метка (my_first_variable в данном случае) принимает значение адреса. Так что эта инструкция поместит адрес переменной в регистр al, но не ее содержимое. Чтобы получить содержимое переменной или любого другого места в памяти нужно заключить его адрес в квадратные скобочки [ и ]. Итак, чтобы скопировать значение нашей переменной в регистр al, используем:

Теперь определим две переменные:Чтобы скопировать значение из variable1 в al мы используем:

А если нужно скопировать al в variable1 тогда:

Чтобы установить значение variable1 (точнее: чтобы установить значение переменной, адрес которой определяется меткой variable1) равным 10 можно попробовать:

но это приведет к ошибке (проверьте, если хотите).

Проблема в том, что мы знаем, что изменяем значение переменной по адресу variable1 на 10, но каков размер переменной? В предыдущих двух случаях был определенный размер — байт. Так как использовался регистр al, размер которого равен байту, то компилятор решил, что и переменная находящаяся по адресу

variable1

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

В этом случае компилятор знает, что помещаемое значение 10 имеет размер один байт, так что решает, что и переменная — то же.

Но может быть сложно постоянно запоминать и указывать размер переменных, когда их используем. Для таких целей можно установить размер переменной при ее определении. Всего лишь написАть оператор размера после имени метки:или

теперь, при использовании

[variable1]

будет иметь то же значение, что и

byte [variable1]

(или

word [variable1]

во втором примере). Так что

mov [variable1], 10

будет работать — в первом случае сохранит байт 10 по адресу

variable1

, во втором — слово, со значением 10.

Нельзя использовать в одной инструкции переменные и значения разного размера:

или

Нельзя использовать в одной инструкции 2 области памяти (за исключением некоторых специальных инструкций). Это не правильно и не будет скомпилировано:

делайте так:

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

ПРИМЕЧАНИЕ: оператор размера, заданный при определении метки имеет меньший приоритет, чем при использовании в инструкции, так что здесь будет использован байт:

в то время как в обычном случае — слово (word):Я думаю Вы обратили внимание, что использование двух строк для определения одной переменной — многовато. Вот более короткий вариант:это то же, что и:

Заметьте, что размер переменной при этом тоже задается. Вообще, если определение данных (с использованием директив

db

или

dw

) начинается с метки, то это определяет так же и саму метку, присваивая ей размер определяемых данных. Так же это работает и для слов:

Пример использования переменных:

5.3. Адреса и основы сегментации

Теперь поговорим об адресах подробнее. Я уже говорил, что адрес — это число (!) которое определяет какую-то позицию в памяти. Мы научились представлять эти числа метками, так что теперь числовые значения адресов — забота компилятора. Но Вы до сих пор ничего не знаете о формате этих чисел. Я попытаюсь разъяснить это в данной главе.

Как вы возможно знаете, данные в памяти хранятся в виде битов, которые могут иметь значение 0 или 1. Можно рассматривать память как одномерный массив битов. 8 идущих друг за другом битов образуют один байт. Адрес — это номер (индекс, позиция в массиве) байта. К примеру адрес 0 — это адрес первого бита в памяти (или адрес первого байта), адрес 1 — это адрес восьмого бита (или адрес второго байта) и так далее.

Адреса в COM-файлах — это числа размером в слово, так чтоне верно. Это может работать если

var1

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

word

, мы поговорим об этом чуть позже.

Теперь некоторый пример с адресами. Исследуйте этот файл:

Здесь адрес соответствующий variable1 = 0, variable2 = 1, variable3 = 2.

ОК. Это выглядит красиво, но это совсем не правда. Проблема в том, что обычно, в одно и тоже время, в памяти загружено много программ (операционная система, драйвер мыши, Ваша программа и так далее). И в этом случай программе необходимо знать, в каком месте памяти она загружена, чтобы она могла добраться до своих переменных. По этой причине адреса являются относительными. Это значит, что для каждой загруженной в память программы резервируется некоторая область, называемая сегмент. Все адреса в памяти, к которым обращается эта программа, в этом случае зависят от начала этой области. Так, [0] не значит первый байт памяти, это значит первый байт сегмента.

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

mov al, [0]

получит доступ к первому байту Вашего сегмента.

ПРИМЕЧАНИЕ

: Я говорил, что адреса в COM-файле — word'ы. Это значит, что они находятся в диапазоне 0..65535. Так, максимальный размер сегмента = 65536 байт. Это можно обойти меняя содержимое сегментных регистров, но не придавайте этому особого значения сейчас.

ПРИМЕЧАНИЕ

: Сегмент — это регион памяти. Но термин сегмент часто используется для адреса начала этого региона. Печально, но факт.

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

word

, называемой офсет или смещение — то есть адрес от начала сегмента.

ПРИМЕЧАНИЕ:

Я говорил, что метки представляют собой адреса переменных. Фактически, метки в FASM'е — это смещения переменных. Вот почему FASM = Flat Assembler (Вы осмыслите это позже (намного позже

)

Я не буду углубляться в сегментные регистры, в то, как хранится в них адрес начала сегмента (здесь есть разница). Примите их как "черный ящик" пока — это работает и мы можем не обращать внимание на них.