Тип word в ассемблере
Типы данных на языке ассемблера и подробные определения данных
Ассемблер распознает набор основных внутренних типов данных (внутренние типы данных) и описывает их типы в соответствии с размером данных (байты, слова, двойные слова и т. Д.), Подписаны ли они, являются ли они целым или действительным числом. Эти типы в значительной степени перекрываются: например, тип DWORD (32-битное целое число без знака) может быть заменен типом SDWORD (32-битное целое число со знаком).
Некоторые люди могут сказать, что программист использует SDWORD, чтобы сообщить читателю, что это значение подписано, но это не обязательно для ассемблера. Ассемблер оценивает только размер операнда. Поэтому, например, программисты могут указывать только 32-разрядные целые числа как типы DWORD, SDWORD или REAL4.
В следующей таблице приведен список всех внутренних типов данных.Символы IEEE в некоторых записях относятся к стандартному формату вещественных чисел, опубликованному IEEE Computer Society.
Заявление об определении данных
Оператор определения данных (оператор определения данных) резервирует место для хранения переменных в памяти и присваивает дополнительное имя. Оператор определения данных определяет переменную в соответствии с внутренним типом данных (таблица выше).
Синтаксис определения данных следующий:
[name] directive initializer [,initializer]…
Ниже приводится пример оператора определения данных:
count DWORD 12345
- Имя: необязательное имя, присвоенное переменной, должно соответствовать спецификации идентификатора.
- Псевдо-инструкция: псевдо-инструкция в операторе определения данных может быть BYTE, WORD, DWORD, SBTYE, SWORD или другими типами, перечисленными в приведенной выше таблице. Кроме того, это также может быть традиционная директива определения данных, как показано в следующей таблице.
В определении данных должно быть хотя бы одно начальное значение, даже если значение равно 0. Остальные начальные значения, если есть, разделяются запятыми. Для целочисленных типов данных начальное значение (инициализатор) представляет собой целочисленную константу или целочисленное выражение, которое соответствует типу переменной, например BYTE или WORD.
Если программист не хочет инициализировать переменную (присвоить значение случайным образом), символ? Можно использовать в качестве начального значения. Все начальные значения, независимо от их формата, ассемблер преобразует в двоичные данные. Начальные значения 0011, 0010b, 32h и 50d имеют одинаковое двоичное значение.
Добавьте переменную в программу AddTwo
Программа AddTwo была представлена в предыдущем разделе «Сложение и вычитание целых чисел», а теперь создана ее новая версия, которая называется AddTwoSum. Эта версия вводит переменную сумму, которая появляется в полном листинге программы:
Вы можете установить точку останова в строке 13, выполнять по одной строке за раз и пошагово выполнять программу в отладчике. После выполнения строки 15 наведите указатель мыши на сумму переменной, чтобы просмотреть ее значение. Или откройте окно Watch.Процесс открытия выглядит следующим образом: выберите Windows в меню Debug (в сеансе отладки), выберите Watch и выберите один из четырех доступных вариантов (Watch1, Watch2, Watch3 или Watch4). Затем с помощью мыши выделите переменную суммы и перетащите ее в окно Watch. На следующем рисунке показан пример, где большая стрелка указывает текущее значение суммы после выполнения строки 15.
Определите данные BYTE и SBYTE
Начальное значение вопросительного знака (?) Делает переменную неинициализированной, что означает присвоение значения переменной во время выполнения:
Необязательное имя - это метка, которая определяет смещение от начала переменной, содержащей раздел, до переменной. Например, если value1 находится по смещению 0000 сегмента данных и занимает один байт в памяти, то value2 автоматически находится по смещению 0001:
value1 BYTE 10h
value2 BYTE 20h
val1 DB 255; беззнаковый байт
val2 DB -128; Байт со знаком
1) Несколько начальных значений
Если в одном определении данных используется несколько начальных значений, его метка указывает только смещение первого начального значения. В следующем примере предположим, что смещение списка равно 0000. Тогда смещение 10 равно 0000, смещение 20 равно 0001, смещение 30 равно 0002, а смещение 40 равно 0003:
list BYTE 10,20,30,40
На следующем рисунке показан список последовательности байтов, показывающий каждый байт и его смещение.
Не все определения данных должны использовать метки. Например, если вы продолжите добавлять массивы байтов после списка, вы можете определить их в следующей строке:
В одном определении данных для его начального значения могут использоваться разные базы. Также можно произвольно комбинировать символы и строковые константы. В следующем примере list1 и list2 имеют одинаковое содержимое:
2) Определить строку
Чтобы определить строку, заключите ее в одинарные или двойные кавычки. Наиболее распространенный тип строки - использование нулевого байта (значение 0) в качестве конечного тега, который называется строкой с завершающим нулем. Этот тип строки используется во многих языках программирования:
Каждый символ занимает один байт памяти. Строки являются исключением из правила, согласно которому значения байтов должны разделяться запятыми. Если бы таких исключений не было, приветствие1 было бы определено как:
greeting1 BYTE 'G', 'o', 'o', 'd'….etc.
Это очень долго. Строку можно разделить на несколько строк, и нет необходимости добавлять метку к каждой строке:
Шестнадцатеричные коды 0Dh и 0Ah также называются CR / LF (возврат каретки и перевод строки) или символы конца строки. При записи стандартного вывода они перемещают курсор в левую часть строки рядом с текущей строкой.
Символ продолжения строки () соединяет две строки исходного кода в оператор, и он должен быть последним символом строки. Следующие утверждения эквивалентны:
greeting1 BYTE "Welcome to the Encryption Demo program "
и
greeting1
BYTE "Welcome to the Encryption Demo program "
Оператор DUP использует целочисленное выражение в качестве счетчика для выделения пространства хранения для нескольких элементов данных. Этот оператор очень полезен при выделении места для хранения строк или массивов. Он может использовать инициализированные или неинициализированные данные:
Определите данные WORD и SWORD
Директивы WORD (слово определения) и SWORD (слово со знаком определения) выделяют место для хранения одного или нескольких 16-битных целых чисел:
Вы также можете использовать традиционную псевдо-инструкцию DW:
Массив 16-битных слов создается путем перечисления элементов или с помощью оператора DUP. Следующий массив содержит набор значений:
myList WORD 1,2,3,4,5
На рисунке ниже представлена схематическая диаграмма массива в памяти при условии, что смещение начальной позиции myList равно 0000. Поскольку каждое значение занимает два байта, приращение его адреса равно 2.
Оператор DUP предоставляет удобный способ объявления массивов:
массив WORD 5 DUP (?); 5 значений, не инициализировано
Определите данные DWORD и SDWORD
Псевдоинструкции DWORD (определение двойного слова) и SDWORD (определение двойного слова со знаком) выделяют место для хранения одного или нескольких 32-битных целых чисел:
Традиционная псевдо-инструкция DD также может использоваться для определения данных двойного слова:
DWORD также можно использовать для объявления переменной, содержащей 32-битное смещение другой переменной. Как показано ниже, pVal содержит смещение val3:
pVal DWORD val3
32-битный массив двойных слов
Теперь определите массив двойных слов и явно инициализируйте каждое его значение:
myList DWORD 1,2,3,4,5
На следующем рисунке показана схематическая диаграмма этого массива в памяти.Предположим, что смещение начальной позиции myList равно 0000, а приращение смещения равно 4.
Определить данные QWORD
Псевдо-инструкция QWORD (определить четыре слова) выделяет место для хранения 64-битных (8 байтов) значений:
quad1 QWORD 1234567812345678h
Традиционная псевдо-инструкция DQ также может использоваться для определения данных из четырех слов:
quad1 DQ 1234567812345678h
Определить сжатые данные BCD (TBYTE)
Intel хранит сжатое двоично-десятичное (BCD, двоично-десятичное) целое число в 10-байтовом пакете. Каждый байт (кроме самого старшего) содержит две десятичные цифры. В младших 9 байтах памяти каждый полубайт хранит десятичное число. В старшем байте самый старший бит представляет бит знака числа. Если старший байт равен 80h, число отрицательное; если старший байт равен 00h, число положительное. Диапазон целых чисел: от -999 999 999 999 999 999 до +999 999 999 999 999 999.
Пример В следующей таблице перечислены шестнадцатеричные байты хранения положительных и отрицательных десятичных чисел 1234, в порядке от младшего до самого старшего байта:
Десятичное значение | Байты памяти |
---|---|
+1234 | 34 12 00 00 00 00 00 00 00 00 |
-1234 | 34 12 00 00 00 00 00 00 00 80 |
MASM использует псевдо-инструкцию TBYTE для определения сжатых переменных BCD. Начальное значение константы должно быть в шестнадцатеричном формате, поскольку ассемблер не будет автоматически преобразовывать начальное десятичное значение в код BCD. В следующих двух примерах показаны допустимые и недопустимые выражения десятичного числа -1234:
Второй пример недействителен, поскольку MASM кодирует константы как двоичные целые числа вместо сжатия целых чисел BCD.
Если вы хотите закодировать действительное число в сжатый код BCD, вы можете использовать инструкцию FLD для загрузки действительного числа в стек регистров с плавающей запятой, а затем использовать инструкцию FBSTP для преобразования его в сжатый код BCD. Эта инструкция округляет значение до ближайшего Целое число:
Если posVal равно 1,5, результирующее значение BCD равно 2.
Определить тип с плавающей запятой
В следующей таблице описано минимальное количество значащих цифр и приблизительные диапазоны стандартных вещественных типов:
тип данных | эффективное число | Приблизительный диапазон |
---|---|---|
Короткий реальный номер | 6 | 1.18x 10-38 to 3.40 x 1038 |
Длинное действительное число | 15 | 2.23 x 10-308 to 1.79 x 10308 |
Действительное число повышенной точности | 19 | 3.37 x 10-4932 to 1.18 x 104932 |
Псевдо-инструкции DD, DQ и DT также могут определять действительные числа:
Ассемблер MASM включает такие типы данных, как wal4 и real8, которые указывают на то, что значение является действительным числом. Чтобы быть более точным, эти значения представляют собой числа с плавающей запятой с ограниченной точностью и диапазоном. С математической точки зрения точность и размер действительных чисел неограниченны.
Программа сложения переменных
До сих пор в примерах программ в этом разделе реализовано целочисленное сложение, хранящееся в регистрах. Теперь, когда у вас есть некоторое представление о том, как определять данные, вы можете изменить ту же программу, чтобы добавить три целочисленные переменные и сохранить сумму в четвертой переменной.
Обратите внимание, что три переменные инициализированы ненулевыми значениями (строки с 9 по 11). Добавьте переменные в строки 16-18. Набор инструкций x86 не позволяет напрямую добавлять одну переменную к другой переменной, но позволяет добавлять одну переменную в регистр. Вот почему EAX используется как аккумулятор в строках 16-17:
mov eax,firstval
add eax,secondval
После строки 17 EAX содержит сумму firstval и secondval. Затем в строке 18 добавьте третье значение к сумме в EAX:
Наконец, в строке 19 сумма копируется в переменную с именем sum:
В качестве упражнения всем рекомендуется запускать эту программу в сеансе отладки и проверять каждый регистр после выполнения каждой инструкции. Окончательная сумма должна быть 53335333 в шестнадцатеричной системе.
Если во время сеанса отладки вы хотите, чтобы переменная отображалась в шестнадцатеричном формате, выполните следующие действия: наведите указатель мыши на переменную или зарегистрируйтесь в течение 1 секунды, пока под курсором мыши не появится серый прямоугольник. Щелкните прямоугольник правой кнопкой мыши и выберите во всплывающем меню «Шестнадцатеричный формат».
Little endian
Процессор x86 хранит и извлекает данные в памяти в обратном порядке (от младшего к большему). Младший байт сохраняется в первом адресе памяти, присвоенном данным, а остальные байты сохраняются в последующих последовательных ячейках памяти. Рассмотрим двойное слово 12345678h. Если он сохраняется по смещению 0000, 78h сохраняется в первом байте, 56h сохраняется во втором байте, а оставшиеся байты сохраняются по смещениям адресов 0002 и 0003, как показано на следующем рисунке. .
Некоторые другие компьютерные системы используют прямой порядок байтов (от старшего к младшему). На следующем рисунке показано, что 12345678h хранится в обратном порядке, начиная со смещения 0000.
Объявить неинициализированные данные
.DATA? Директива объявляет неинициализированные данные. При определении большого количества неинициализированных данных директива .DATA? Уменьшает размер компилятора. Например, следующий код является допустимым утверждением:
С другой стороны, скомпилированная программа, сгенерированная следующим кодом, будет иметь дополнительные 20 000 байтов:
Гибридный ассемблер кода и данных позволяет переключать код и данные в программе туда и обратно. Например, вы хотите объявить переменную, чтобы ее можно было использовать только в локальной области программы. В следующем примере между двумя операторами кода вставляется переменная с именем temp:
Хотя присутствие оператора temp прерывает поток исполняемых инструкций, MASM поместит temp в раздел данных и отделит его от раздела кода, который остается скомпилированным. Однако в то же время смешивание директив .code и .data может затруднить чтение программы.
Переменные хранятся в памяти по определенным адресам. Программисту проще иметь дело именами переменных, чем с адресами в памяти. Например, переменная с именем "var1" будет более понятна в коде программы, чем адрес 5A73:235B, особенно когда количество переменных велико.
Наш компилятор поддерживает два типа переменных: BYTE и WORD.
имя DB значение
имя DW значение
DB - Define Byte - определяет байт.
DW - Define Word - определяет слово.
имя - может быть любой комбинацией букв или цифр, но должно начинаться с буквы. Можно объявлять безымянные переменные, которые имеют адрес, но не имеют имени.
Как вы уже знаете из части 2 этих уроков, команда MOV используется для копирования значения из источника в приемник.
Давайте посмотрим другой пример с командой MOV:
Скопируйте вышеприведенный код в редактор кода Emu8086 и нажмите клавишу F5, чтобы откомпилировать и загрузить этот код в эмулятор. Вы увидите примерно такую картину:
На рисунке вы можете заметить команды, похожие на те, что используются в нашем примере. Только переменные заменены фактическими местоположениями в памяти. Когда компилятор создает машинный код, он автоматически заменяет имена всех переменных их смещениями. По умолчанию сегмент загружен в регистр DS (в COM-файлах значение регистра DS устанавливается таким же, что и значение в регистре CS - сегменте кода).
В таблице памяти (memory) первый столбец - это смещение, второй столбец - это шестнадцатиричное значение, третий столбец - десятичное значение, а последний столбец - это символ ASCII, соответствующий данному числу.
Компилятор не чувствителен к регистру, поэтому "VAR1" и "var1" - это одно и то же.
Смещение переменной VAR1 - это 0108h, а полный адрес - 0B56:0108.
Смещение переменной var2 - это 0109h, а полный адрес - 0B56:0109. Эта переменная имеет тип WORD, поэтому занимает 2 БАЙТА. Принято младший байт записывать по меньшему адресу, поэтому 34h размещается перед 12h.
Вы можете увидеть некоторые другие инструкции после команды RET. Это случается потому, что дизассемблер не знает, где начинаются данные. Он только обрабатывает значения в памяти и понимает их как имеющие силу инструкции процессора 8086 (мы изучим их позже).
Вы можете даже написать программу, используя только директиву DB:
Скопируйте вышеприведенный код в редактор кода Emu8086 и нажмите клавишу F5, чтобы откомпилировать и загрузить этот код в эмулятор. Вы получите тот же самый дизассемблированный код и тот же самый результат работы программы!
Как вы можете догадаться, компилятор только преобразует исходный код программы в набор байтов. Этот набор байтов называется машинным кодом. Процессор обрабатывает машинный код и выполняет его.
ORG 100h - это директива компилятора (она указывает компилятору как обрабатывать исходный код). Эта директива очень важна при работе с переменными. Она указывает компилятору, какой исполняемый файл будет загружаться в смещение (offset) 100h (256 байтов), так что компилятор должен вычислить правильный адрес для всех переменных, когда он размещает имена переменных с их смещениями. Директивы никогда не преобразуются в какой-либо реальный машинный код. Почему исполняемый файл загружается по смещению 100h? Операционная система хранит некоторые данные о программе в первых 256 байтах, начиная от CS (сегмента кода), такие как параметры командной строки и т.д. Все это справедливо только для COM-файлов, файлы EXE загружаются по смещению 0000, и обычно используют специальный сегмент для переменных. Может быть, мы поговорим об EXE-файлах позже.
Массив можно рассматривать как цепочку переменных. Текстовая строка - это пример массива байтов, в котором каждый символ представлен значением ASCII-кода (0..255).
Вот некоторые примеры определения массивов:
a DB 48h, 65h, 6Ch, 6Ch, 6Fh, 00h
b DB 'Hello', 0
b - это точная копия массива a - когда компилятор видит строку, заключенную в кавычки, он автоматически преобразует ее в набор байтов. Эта таблица показывает участок памяти, где эти массивы объявлены:
Вы можете получить значение любого элемента массива, используя квадратные скобки, например:
Вы можете также использовать какой-либо из регистров BX, SI, DI, BP, например:
MOV SI, 3
MOV AL, a[SI]
Если необходимо объявить большой массив, вы можете использовать оператор DUP.
Синтаксис для DUP:
количество DUP ( значение(я) )
количество - количество дубликатов (любая константа).
значение - выражение, которое будет дублироваться оператором DUP.
а это альтернативный способ объявления:
еще один пример:
а это альтернативный способ объявления:
d DB 1, 2, 1, 2, 1, 2, 1, 2, 1, 2
Конечно, вы можете использовать DW вместо DB, если требуется хранить числа более 255, или менее -128. DW не может быть использован для объявления строк!
Оператор DUP не может содержать более 1020 знаков в качестве операнда! (в последнем примере 13 знаков). Если вам необходимо объявить очень большой массив, разделите его на две строки (вы получите один большой массив в памяти).
Получение адреса переменной
Есть такая команда LEA (Load Effective Address) и альтернативный оператор OFFSET. Как OFFSET так и LEA могут быть использованы для получения смещения адреса переменной.
LEA более мощная, т.к. она также позволяет вам получить адрес индексированных переменных. Получение адреса переменной может быть очень полезно в различных ситуациях, например, если вам необходимо поместить параметр в процедуру.
Напоминание:
Чтобы указать компилятору тип данных, вы должны использовать следующие префиксы:
BYTE PTR - для байта.
WORD PTR - для слова (два байта).
Например:
Emu8086 поддерживает короткие префиксы:
b. - для BYTE PTR
w. - для WORD PTR
иногда компилятор может вычислить тип данных автоматически, но вы не можете и не должны полагаться на это, если один из операндов является непосредственным значением.
Здесь первый пример:
Здесь другой пример, который использует OFFSET вместо LEA:
Оба примера функционально идентичны.
LEA BX, VAR1
MOV BX, OFFSET VAR1
даже компилируются в одинаковый машинный код: MOV BX, num
num - это 16-битовое значение смещения переменной.
Пожалуйста учтите, что только эти регистры могут использоваться внутри квадратных скобок (как указатели памяти):
BX, SI, DI, BP!
(См. предыдущую часть уроков).
Константы подобны переменным, но они существуют до того, как ваша программа откомпилирована (ассемблирована). После определения константы ее значение не может быть изменено. Для определения константы используется директива EQU:
Этот пример функционально идентичен коду:
Вы можете наблюдать переменные во время выполнения программы, если выберите пункт "Variables" в меню "View" эмулятора.
Чтобы наблюдать массивы, вы должны щелкнуть по переменной и установить свойство Elements - размер массива. В Ассемблере нет строгих типов данных, поэтому любые переменные могут быть представлены как массив.
- HEX - шестнадцатиричная (основа 16).
- BIN - двоичная (основа 2).
- OCT - восмеричная (основа 8).
- SIGNED - десятичная со знаком (основа 10).
- UNSIGNED - десятичная без знака (основа 10).
- CHAR - коды ASCII-символов (всего 256 символов, некоторые символы невидимы).
Можно вводить числа в любой системе, шестнадцатиричные цифры должны иметь суффикс "h", двоичные - суффикс "b", восмеричные - суффикс "o", десятичные цифры не требуют суффикса. Строка может быть введена следующим способом:
'hello world', 0
(эта строка заканчивается нулем).
Массив может быть введен следующим способом:
(массив может быть массивом байтов или слов, это зависит от того, выбран ли BYTE или WORD для введенной переменной).
В некоторых примерах программ используются русские буквы для вывода на экран. Это сделано только для удобства чтения текстов программ. Если просто скопировать такой файл в текстовый редактор, то после компиляции и запуска программы на экране вы увидите "иероглифы". Это связано с тем, что большинство компиляторов "не понимают" русские буквы в кодировке Windows. Эту проблему можно решить, например, используя редактор DOS, или (для Ассемблера) вставляя в строку не буквы, а их ASCII-коды (например: 143, 224, 168, 162, 165, 226, '$' - это будет строка "Привет")
Функция 01h (Считать символ из STDIN с эхом, ожиданием и проверкой на Ctrl+Break)
Ввод расширенных клавиш ASCII (F1-F12, PageUp и т.п.) требует двух обращений к этой функции. Первый вызов возвращает AL=0, второй вызов возвращает в AL расширенный код ASCII.
Функция 02h (Вывести символ на STDOUT с проверкой на Ctrl+Break)
Данная функция обрабатывает некоторые управляющие символы:
Выводимый символ | Действие |
BEL (07h) | появляется звуковой сигнал |
BS (08h) | курсор перемещается на одну позицию влево |
НТ (09h) | используется для замены на несколько пробелов |
LF (0Ah) | перевод курсора на одну позицию вниз |
CR (0Dh) | переход на начало текущей строки |
Пример:
Функция 06h (Записать символ в STDOUT без проверки на Ctrl+Break)
Если DL = FFh, то эта функция считывает символ из STDIN без эха, без ожидания и без проверки на Ctrl+Break. При этом после выполнения прерывания ZF = 1 и AL = 0, если НЕ была нажата клавиша; ZF = 0 и AL = код символа, если клавиша была нажата.
Функция 07h (Считать символ из STDIN без эха, с ожиданием и без проверки на Ctrl+Break)
Для ввода расширенного ASCII-кода эту функцию необходимо вызвать дважды (см. для примера функцию 01h). Для проверки статуса (если нет необходимости ожидать нажатие клавиши) следует использовать функцию 0Вh.
Функция 08h (Считать символ из STDIN без эха, с ожиданием и проверкой на Ctrl+Break)
Для ввода расширенного ASCII-кода эту функцию необходимо вызвать дважды (см. для примера функцию 01h).
Функция 09h (Записать строку в STDOUT с проверкой на Ctrl+Break)
Функция 0Аh (Считать строку символов из STDIN в буфер)
Пример: Символы считываются до нажатия на клавишу Enter или до достижения максимальной длины. Обратите внимание, что в этом примере для буфера выделяется 6 байт, в то время как считано будет только ПЯТЬ видимых символов. Символ конца строки (CR) не учитывается при вычислении длины строки, однако, для него необходимо выделять память.
Функция 0Вh (Проверить состояние клавиатуры)
Пример: Эта функция не обрабатывает такие клавиши, как Shift, Ctrl, Alt и т.д. Однако сочетание клавиш Ctrl+Break обрабатывается.
Функция 0Сh (Очистить буфер и считать символ)
Функция 0Eh (Установить текущий диск DOS)
В регистр AL возвращается число дисководов всех типов, включая жесткие и логические диски.
Функция 19h (Получить текущий диск DOS)
Функция 1Bh (Получить информацию FAT (текущий диск))
Эта функция изменяет содержимое регистра DS.
Пример программы см. в описании функции 1Сh.
Функция 1Сh (Получить информацию FAT (любой диск))
Функция 2Аh (Получить системную дату)
См. пример в описании функции 2Сh.
Функция 2Вh (Установить системную дату)
Функция 2Ch (Получить время)
Функция 2Dh (Установить время)
Функция 39h (Создать директорию)
Функция 3Аh (Удалить директорию)
Функция 3Вh (Сменить директорию)
Функция 3Ch (Создать файл)
Если файл уже существует, эта функция все равно открывает его, присваивая ему нулевую длину. Во избежание этого следует пользовать функцией 5Вh.
Пример: См. также пример для функции 3Fh.
Функция 3Dh (Открыть существующий файл)
Файл должен существовать.
См. также пример для функции 3Fh.
Функция 3Eh (Закрыть файл)
См. также пример для функции 3Fh.
Функция 3Fh (Чтение из файла или устройства)
- если АХ = СХ (и CF = 0) - чтение было корректным и без ошибок
- если АХ = 0 - достигнут конец файла (EOF)
- если AX
Встроенный ассемблер может также использоваться для написания полноценных процедур на языке Ассемблера. Такие процедуры должны иметь ключевое слово Assembler, добавленное после заголовка процедуры. Эта функция использует особенность процессора i80386 для очень быстрого умножения на 9.
Процедуры Ассемблера отличаются от стандартных процедур Паскаля следующими деталями:
Нет возвращения переменной
Структурированные аргументы (такие как строки, объекты, записи) не копируются в локальные переменные. Они должны быть обработаны как параметры Var.
Граница стека
Процедуры Ассемблера не имеют границ стека, если они не содержат аргументов и локальных символов. Вообще, граница стека во встроенном ассемблере, это Здесь Locals - это общий размер локальных параметров, Params - общий размер параметров процедуры.
Сохранение регистров
Код Ассемблера не должен изменять значение следующих регистров: DS, CS, SS, ES, EBP и ESP. Все другие регистры могут перезаписываться. Учитывайте включение регистра ES. TMT Паскаль всегда предполагает, что ES равен DS.
Кроме того, вы не должны изменять сегмент, страницу и таблицу прерываний, также как управление, отладку и тестирование регистров, если вы не очень хорошо знакомы с архитектурой 386-го в защищенном режиме. Привилегированные команды, подобные LGDT и LIDT, поддерживаются встроенным ассемблером. Однако, избегайте использовать их, если вы не уверены в своих действиях.
Кодовая процедура
Помимо процедур ассемблерных, вы можете использовать кодовые процедуры. Они имеют следующие отличия: компилятор не исполняет команду ограничения на ввод и возвращает из процедуры (включая команду RET), а локальные параметры являются основанными на ESP на момент ввода.
Синтаксис команд
Общий синтаксис операторов ассемблера
[label:]
[prefixes]
[[opcode [operand1 [,operand2 [,operand3]]]]
Метки
-
Глобальные метки объявляются в программе Паскаля в пределах описания метки. Локальные метки не объявляются. Они должны начинаться с символа @ и содержать буквы, цифры или символ подчеркивания. Локальные метки имеют силу только в пределах операторных скобок asm.
Префиксы
Префиксы являются модификаторами для следующих команд. ТМТ Паскаль позволяет следующие мнемоники префиксов:Коды операций
Код операции - это мнемоническая команда или директива ассемблера. Список поддерживаемых "опкодов" приведен ниже. ТМТ Паскаль допускает только следующие директивы ассемблера: DB, DW и DD.
Пример: Директивы DB, DW и DD позволяют объявить некоторое количество аргументов, разделенных запятыми. Другие обычно используемые директивы ассемблера могут быть эмулированы при помощи операторов Паскаля. Например, директива EQU может быть заменена на const, а STRUCT может быть определена описанием типа record.
Регистры
Следующие регистры могут быть использованы встроенным ассемблером: Сегментный регистр может использоваться для отмены сегмента. 32-битные регистры могут использоваться для индексации на процессорах 80386 и выше. 16-битные регистры никогда не должны использоваться для адресации, если ваша программа превышает размер 64кБ. Но и в том случае, когда программа имеет размер менее 64кБ, 16-битная адресация неэффективна. Вообще, адрес формируется так:
Базовый адрес + Индекс * Масштаб + Смещение
Где "Базовый адрес" - это любой 32-битный регистр, "Индекс" - любой 32-битный регистр, кроме ESP, "Масштаб" должен быть 1, 2, 4 или 8. "Смещение" - целое число.
Ниже приведены некоторые правильные и неправильные способы адресации: Подробности ищите в справочных руководствах программиста Intel™ 80386/80486.
Мнемонические коды операций
Этот раздел представляет список значений мнемонических кодов операций. Подробности ищите в справочных руководствах программиста 80386. В списке используются следующие сокращения:Выражения операндов
Выражения операндов строятся из операндов и операторов. Операнды - это константы, регистры, метки и адреса памяти. Операторы комбинируют операндами и изменяют их свойства. Каждая команда допускает только некоторые комбинации операндов.
-
Непосредственные операнды или константы. Регистры. Операнды памяти и метки.
Встроенный ассемблер ТМТ Паскаля позволяет использовать 8, 16 или 32-битные 80386 регистры. Операнды памяти и метки
Операнды памяти ссылаются на данные, записанные в памяти. Обычно используются квадратные скобки [..] или оператор типа ptr - это гарантирует, что аргумент будет обработан как адрес памяти. Операнды-метки ссылаются на определенное место в коде. Память и непосредственные операнды могут быть либо абсолютными, либо неопределенными. Абсолютный операнд - это операнд, значение или смещение которого известны во время компиляции. Неопределенный операнд - это операнд, смещение которого станет известно только во время компоновки.
Операнды
ТМТ Паскаль допускает следующие операнды: Числовые константы, Строки, Регистры, Символы Паскаля и специальные символы.
Числовые константы
Числовые константы это 32-битные целые числа, например, целое число в диапазоне -2147483648..4294967295. Числовые константы могут вводиться как десятичные числа, двоичные числа (используется суффикс 'B'), восмеричные числа (используются суффиксы 'Q' или 'O'), шестнадцатиричные числа (используются суффикс 'H' или префикс '$'). Учтите, что шестнадцатиричные числа должны начинаться с цифры.
Строки заключаются в одинарные или двойные кавычки. Если между кавычками встречается кавычка такого же типа, то она обрабатывается как символ. Строковые константы произвольной длины могут иметь место только в директиве 'DB'. Во всех других случаях строка не должна превышать четырех символов и ее значение преобразуется в целое число.
Использование регистров было описано выше.
Символы Паскаля
Со встроенным ассемблером Вы можете обращаться к большинству символов Паскаля. Они включают метки, константы, переменные, типы и процедуры. Значения, классы и типы символов Паскаля сведены в таблицу: Специальные Символы Ассемблера
Встроенный ассемблер поддерживает пять специальных символов: @CODE, @DATA, @RESULT, @PARAMS и @LOCALS. @CODE и @DATA в реальности не используются в плоской модели. Они всегда возвращают 0Ch и 14h, которые являются стандартными селекторами сегментов для сегментов данных и кода. Символ @RESULT указывает на псевдопеременную, которая содержит возвращенный функцией результат, @PARAMS и @LOCALS возвращают размер параметра и локальных областей в стеке.
Операторы
Встроенный Ассемблер ТМТ Паскаля допускает операторы, перечисленные в следующем списке:
& | Оператор отмены Идентификатора. Следующий идентификатор рассматривается как определенный пользователем символ, даже если он совпадает с зарезервированным словом ассемблера. |
( ) | Круглые скобки. Выражения, заключенные в круглые скобки имеют приоритет и выполняются первыми. |
[ ] | Указатель памяти. Выражение в квадратных скобках обрабатывается первым. Это выражение должно быть значением адреса, принятым в архитектуре процессора 386. Результат выражения всегда обрабатывается как адрес памяти. |
HIGH, LOW | Выбор старшего и младшего байта. Эти операторы возвращают старшие и младшие 8 бит выражения, которое имеет размер слова и следует после этих операторов. |
+, - | Операторы унарного сложения и вычитания. Выражение должно быть абсолютным непосредственным. |
SMALL, LARGE | Заставляет встроенный ассемблер обрабатывать последующие операнды как 16-ти или 32-битные. |
OFFSET | Возвращает 32-битное смещение следующего за ним выражения. |
SEG | Возвращает сегментную часть операнда. |
TYPE | Возвращает тип операнда. Тип символа NEAR - это -1, тип символа FAR - это -2. Тип операнда памяти - это его размер. Тип непосредственного значения - 0. |
PTR | Преобразует следующее за ним выражение в тип, который указан перед оператором PRT. Допустимые значения: BYTE PRT, WORD PRT, DWORD PRT, FWORD PRT, TBYTE PRT, QWORD PRT, NEAR PRT и FAR PRT. |
* | Умножение. Оба аргумента должны быть непосредственными значениями, или один из аргументов должен быть индексирующим регистром, а другой - масштабирующим множителем (1,2,4 или 8). |
/ | Деление. Оба аргумента должны быть непосредственными значениями. |
MOD | Остаток от целого деления. Оба аргумента должны быть непосредственными значениями. |
SHL, SHR | Сдвиг влево и сдвиг вправо. Оба аргумента должны быть непосредственными значениями. |
+, - | Сложение и вычитание. Больший из аргументов может быть неопределенным значением, но это не может быть вычитаемый аргумент. Другой аргумент должен быть абсолютным непосредственным значением. |
NOT | Двоичное дополнение. Аргумент должен быть абсолютным непосредственным значением. |
AND | Поразрядное И. Аргумент должен быть абсолютным непосредственным значением. |
OR | Поразрядное ИЛИ. Аргумент должен быть абсолютным непосредственным значением. |
XOR | Поразрядное ИСКЛЮЧАЮЩЕЕ ИЛИ. Аргумент должен быть абсолютным непосредственным значением. |
Приоритет операторов Ассемблера
Приоритет этих операторов показан в следующей таблице (от самого высокого приоритета до самого низкого):Различия между 16-ти и 32-разрядным кодом
Это руководство не обучает 32-битному программированию. Мы только перечислим здесь несколько соображений, важных при создании 32-битных программ на ассемблере. Эти соображения могут быть полезны для программиста на 16 битов, который начинает осваивать 32-битное программирование.
Избегайте использовать 16-битные регистры для индексации.
Встроенный ассемблер будет правильно обрабатывать команды, подобные этим: Однако, эти программы не будут работать правильно, если размер вашей программы превышает 64КБ и "таблица" переменных размещена за пределами этого диапазона. Это потому, что 16-битные адреса охватывают только сегмент, размером 64КБ. Последний из приведенных примеров наиболее опасен, т.к. существует вероятность разрушения системы.
Jump таблицы
Jump таблицы должны строиться как таблицы 32-битных адресов, а не 16-битные адреса.
"Длинная" (32-bit) Арифметика
Старайтесь использовать "длинную" арифметику как можно чаще. 16-битные команды часто занимают больше места, чем соответствующие 32-битные команды. В коде было бы лучше заменить первую команду на которая является на один байт короче. Кроме того, если data1 и data2 могут быть заменены на 32-битные значения, вы можете сохранить больше места (и времени) в программе как в разделе Ассемблера, так и в разделе Паскаля.
ECX против CX
Циклы и команды повторения в 32-битном режиме используют регистр ЕСХ быстрее, чем СХ. Следующий сегмент программы, возможно, создаст проблемы: Также учтите, что регистры индексов источника и приемника ESI и EDI быстрее, чем SI и DI.
Используйте POPAD и PUSHAD вместо POPA и PUSHA. Последние помещают в стек только 16 бит.
Используйте POPFD и PUSHFD вместо POPF и PUSHF. Последние помещают в стек только 16 бит.
Используйте 'IRETD' вместо 'IRET'. Последняя "выталкивает" 16-битные регистры.
Строковые команды
При выполнении операций со строками, используйте команды с двойным словом вместо байта или слова. Используйте MOVSD вместо MOVSW или MOVSB.
JECXZ против JCXZ
Различают команды JCXZ и JECXZ. Первая проверяет регистр СХ, в то время как вторая проверяет регистр ЕСХ. Использование JCXZ вместо JECXZ может привести к труднонаходимым ошибкам. Точно также LOOP проверяет ECX, а LOOP16 проверяет CX.
Результаты функций
Помните, что 32-битный результат нужно возвращать в ЕАХ, а не DX:AX.
ES: сохранение
Не изменяйте значение регистра ES. TMT Pascal определяет ES = DS.
Immediate PUSH
TMT Pascal предполагает, что в стек помещается непосредственное значение командой, подобной этим: Заметьте, что подобно TASM и в отличие от ассемблера PharLap, TMT Pascal обработает команду Как будто это Var-параметры
Подобно 16-битному режиму, var-параметры - это 32-битные указатели. Однако, в ТМТ Паскале указатели являются только 32-битным смещением в пределах сегмента данных. Поэтому var-параметры возвращаются командой MOV, а не LES или LDS.
Локальные символы
Локальные символы и параметры адресованы через регистр ЕВР. Например, в последняя строка ассемблируется в
Читайте также: