мета-данные страницы
Урок 6
Работа с памятью
Сегмент данных предназначен для определения констант, рабочих полей и областей для вводв-вывода. В соответствии с имеющимися директивами в ассемблере разрешено определение данных различной длины: например, директива DB определяет байт, а директива DW oпределяет слово. Элемент данных может содержать непосредственное значение или константу, определенную как символьная строка или как числовое значение. Другим способом определения константы является непосредственное значение, т.е. указанное прямо в ассемблерной команде, например:
MOV AL,20H
В этом случае шестнадцатеричное число 20 становится частью ма шинного объектного кода. Непосредственное значение ограничено oдним байтом или одним словом, но там, где оно может быть применено, оно является более эффективным, чем использование конcтанты.
Директивы определения данных
Ассемблер обеспечивает два способа определения данных: во-первых, через указание длины данных и, во-вторых, по их cодержимому. Рассмотрим основной формат определения данных:
[имя] Dn выражение
- Имя элемента данных не обязательно (это указывается
квадратными скобками), но если в программе имеются ссылки на некоторый элемент, то это делается посредством имени. Правила написания имен приведены в разделе «Формат кодирования» в главе 3. - Для определения элементов данных имеются следующие директивы: DB (байт), DW (слово), DD (двойное слово), DQ (учетверенное слово) и DT (десять байт). - Выражение может содержать константу, например:
FLD1 DB 25
или знак вопроса для неопределенного значения, например
FLDB DB ?
Выражение может содержать несколько констант, разделенных запятыми и ограниченными только длиной строки:
FLD3 DB 11, 12, 13, 14, 15, 16, ...
Ассемблер определяет эти константы в виде последовательности cмежных байт. Ссылка по имени FLD3 указывает на первую константу, 11, по FLD3+1 - на вторую, 12. (FLD3 можно представить как FLD3+0). Например команда
MOV AL,FLD3+3
загружает в регистр AL значение 14 (шест. 0E). Выражение допускает также повторение константы в следующем формате:
[имя] Dn число-повторений DUP (выражение) ...
Следующие три примера иллюстрируют повторение:
DW 10 DUP(?) ;Десять неопределенных слов DB 5 DUP(14) ;Пять байт, содержащих шест.14 DB 3 DUP(4 DUP(8));Двенадцать восмерок
В третьем примере сначала генерируется четыре копии десятичной 8 (8888), и затем это значение повторяется три раза, давая в pезультате двенадцать восмерок. Выражение может содержать символьную строку или числовую константу.
Символьные строки
Символьная строка используются для описания данных, таких как, например, имена людей или заголовки страниц. Содержимое строки oтмечается одиночными кавычками, например, 'PC' или двойными кавычками - «PC». Ассемблер переводит символьные строки в объектный код в обычном формате ASCII. Символьная строка определяется только директивой DB, в котоpой указывается более двух символов в нормальной последовательности слева направо. Следовательно, директива DB представляет единственно возможный формат для определения символьных данных.
Числовые константы
Числовые константы используются для арифметических величин и для aдресов памяти. Для описания константы кавычки не ставятся. Ассемблер преобразует все числовые константы в шестнадцитеричные и записывает байты в объектном коде в обратной последовательности - справа налево. Ниже показаны различные числовые форматы.
Десятичный формат
Десятичный формат допускает десятичные цифры от 0 до 9 и обозначается последней буквой D, которую можно не указывать, например, 125 или 125D. Несмотря на то, что ассемблер позволяет кодирование в десятичном формате, он преобразует эти значения в шест. объектный код. Например, десятичное число 125 преобразуется в шест. 7D.
Шестнадцатиричный формат
Шестнадцатиричный формат допускает шест. цифры от 0 до F и обозначается последней буквой H. Так как ассемблер полагает, что с буквы начинаются идентификаторы, то первой цифрой шест. константы должна быть цифра от 0 до 9. Например, 2EH или 0FFFH, которые ассемблер преобразует соответственно в 2E и FF0F (байты во втором примере записы ваются в объектный код в обратной последовательности).
Двоичный формат
Двоичный формат допускает двоичные цифры 0 и 1 и обозначается последней буквой B. Двоичный формат обычно используется для более четкого представления битовых значений в логических командах AND, OR, XOR и TEST. Десятичное 12, шест. C и двоичное 1100B все генерируют один и тот же код: шест. 0C или двоичное 0000 1100 в зависимости от того, как вы рассматриваете содержимое байта.
Восмеричный формат
Восмеричный формат допускает восмеричные цифры от 0 до 7 и обозначается последней буквой Q или O, например, 253Q. На сегодня восмеричный формат используется весьма редко.
При записи символьных и числовых констант следует помнить, что, например, символьная константа, определенная как DB '12', представляет символы ASCII и генерирует шест. 3132, а числовая константа, oпределенная как DB 12, представляет двоичное число и генерирует шест. 0C.
Директива опеделения байта
Из различных директив, определяющих элементы данных, наиболее полезной является DB (определить байт). Символьное выражение в диpективе DB может содержать строку символов любой длины, вплоть до конца строки. Числовое выражение в директиве DB может содержать одну или более однобайтовых констант. Один байт выражается двумя шест. цифpами. Наибольшее положительное шест. число в одном байте это 7F, если подразумевается, что это число знаковое, и 0FF, если подразумевается, что число беззнаковое
Директива определения слова (DW)
Директива DW определяет элементы, которые имеют длину в одно слово (два байта). Символьное выражение в DW ограничено двумя символами, которые ассемблер представляет в объектном коде так, что, например, 'PC' становится 'CP'. Для определения символьных строк директива DW имеет ограниченное применение. Числовое выражение в DW может содержать одно или более двухбайтовых констант. Два байта представляются четырьмя шест. цифрами. Наибольшее положительное шест. число в двух байтах это 7FFF; все «большие» числа от 8000 до FFFF представляют отрицательные значения. В десятичном исчислении эти пределы выражаются числами +32767 и -32768. Для форматов директив DW, DD и DQ ассемблер преобразует константы в шест. объектный код, но записывает его в обратной последовательности. Таким образом десятичное значение 12345 преобразуется в шест.3039, но записывается в объектном коде как 3930.
Директива определения двойного слова (DD)
Директива DD определяет элементы, которые имеют длину в два cлова (четыре байта). Числовое выражение может содержать одну или более констант, каждая из которых имеет максимум четыре байта (восемь шест. цифр). Наибольшее положительное шест. число в четырех байтых это 7FFFFFFF; все «большие» числа от 80000000 до FFFFFFFF представляют отрицательные значения. В десятичном исчислении эти пределы выражаются числами +2147483647 и -2147483648. Ассемблер преобразует все числовые константы в директиве DD в шест. представление, но записывает объектный код в обратной последовательности. Таким образом десятичное значение 12345 преобразуется в шест. 00003039, но записывается в oбъектном коде как 39300000.
Директива определения учетверённого слова (DQ)
Директива DQ определяет элементы, имеющие длину четыре слова (восемь байт). Числовое выражение может содержать одну или более констант, каждая из которых имеет максимум восемь байт или 16 шест.цифр. Наибольшее положительное шест. число - это семерка и 15 цифр F. Для получения представления о величине этого числа, покажем, что шест. 1 и 15 нулей эквивалентен следующему десятичному числу:
1152921504606846976
Ассемблер преобразует все числовые константы в директиве DQ в шест. представление, но записывает объектный код в обратной последовательности, как и в дирек- тивах DD и DW. Обработка ассемблером символьных строк в директиве DQ aналогично директивам DD и DW.
Директива определения десяти байт (DT)
Директива DT определяет элементы данных, имеющие длину в десять байт. Назначение этой директивы связано с «упакованными десятичными» числовыми величинами. По директиве DT генерируются различные константы, в зависимости от версии ассемблера.
Непосредственные операнды
Команда
MOV AX,0123H
пересылает непосредственную шест. константу 0123 в регистр AX. Трехбайтный объектный код для этой команды есть B82301, где B8 обозначает «переслать непосредственное значение в регистр AX», a следующие два байта содержат само значение. Многие команды имеют два операнда: первый может быть регистр или адрес памяти, а второй - непосредственная константа. Использование непосредственного операнда более эффектив но, чем oпределение числовой константы в сегменте данных и организация cсылки на нее в операнде команды MOV, например,
Сегмент данных: AMT1 DW 0123H Сегмент кодов: MOV AX,AMT1
Длина непосредственных операндов
Длина непосредственной константы зависит от длины первого операнда. Например, следующий непосредственный операнд является двухбайтовым, но регистр AL имеет только один байт:
MOV AL,0123H (ошибка)
однако, если непосредственный операнд короче, чем получающий операнд, как в следующем примере
ADD AX,25H (нет ошибки)
то ассемблер расширяет непосредственный операнд до двух байт, 0025 и записывает объектный код в виде 2500.
Непосредственные форматы
Непосредственная константа может быть шестнадцатиричной, напpимер, 0123H; десятичной, например, 291 (которую ассемблер конвертирует в шест.0123); или двоичной, например, 100100011В (которая преобразуется в шест. 0123). Ниже приведен список команд, которые допускают непосредственные операнды:
Команды пересылки и сравнения: MOV, CMP. Арифметические команды: ADC, ADD, SBB, SUB. Команды сдвига: RCL, RCR, ROL, ROR, SHL, SAR, SHR. Логические команды: AND, OR, TEST, XOR.
Директива EQU
Директива EQU не определяет элемент данных, но определяет значение, которое может быть использовано для постановки в других командах. Предположим, что в сегменте данных закодирована следующая директива EQU:
TIMES EQU 10
Имя, в данном случае TIMES, может быть представлено любым допустимым в ассемблере именем. Теперь, в какой-бы команде или директиве не использовалось слово TIMES ассемблер подставит значение 10. Например, ассемблер преобразует директиву
FIELDA DB TIMES DUP (?)
в
FIELDA DB 10 DUP (?)
Имя, связанное с некоторым значением с помощью директивы EQU, может использоваться в командах, например:
COUNTR EQU 05
... MOV CX,COUNTR
Ассемблер заменяет имя COUNTR в команде MOV на значение 05, cоздавая операнд с непосредственным значением, как если бы было закодировано
MOV CX,05 ;Ассемблер подставляет 05
Здесь приемущество директивы EQU заключается в том, что многие команды могут использовать значение, определенное по имени COUNTR. Если это значение должно быть изменено, то изменению подлежит лишь одна директива EQU. Естественно, что использование директивы EQU разумно лишь там, где подстановка имеет смысл для ассемблера. В директиве EQU можно использовать символические имена:
1. TP EQU TOTALPAY 2. MPY EQU MUL
Первый пример предполагает, что в сегменте данных программы опpеделено имя TOTALPAY. Для любой команды, содержащей операнд TP, ассемблер заменит его на адрес TOTALPAY. Второй пример показывает возможность использования в программе слова MPY вместо обычного мнемокода MUL.
Работа с файлами в MS-DOS
Функции типа дескриптора
В MS-DOS начиная со второй версии файловая система стала иерархической, т.е. появились каталоги (или их можно назвать папками, или директориями). С иерархической файловой системой вы знакомы из опыта общения с OS Windows. Полное имя файла представляет из себя обозначение дисковода, путь до файла и имя файла. По ограничениям MS-DOS размер имени файла не может превышать 8 символов на собственно имя и 3 символов на расширение. Полное имя файла в MS-DOS можно представить, например, так:
C:\SYSTEM\COMMAND.COM
Специфика MS-DOS такова, что в имени файла регистр не учитывается, то есть вы можете записать как COMMAND.COM, так и command.com. Приведённый пример вообще говоря представляется в виде ASCIIZ последовательности - это значит, что каждому символу соответствует число из таблицы кодов ASCII символов, а также в конец строки добавляется число 0, то есть запись
C:\SYSTEM\COMMAND.COM
преобразуется в
43 3a 5c 53 59 53 54 45 4d 5c 43 4f 4d 4d 41 4e 44 2e 43 4f 4d 00
Для работы с файлом с помощью функции типа дескриптора, нужно поместить адрес имени файла в DS:DX. В регистр ah помещается номер функции, после чего вызывается прерывание int 21H. Все функции типа дескриптора после выполнения возвращают опущенный флаг переноса, если функция выполнена успешно и наоборот. Чтобы совершить переход к вашему обработчику ошибки, выполните команду
jc error
где error - указатель на начало обработчика. Теперь рассмотрим некоторые функции типа дескриптора, которые нам сегодня пригодятся.
Создание файла
По заданному в коде ASCIIZ пути создаёт новый файл на указанном или текущем диске или на указанном или текущем каталоге. Если указанный файл существует, то он усекается до нулевой длины. В любом случае открывается файл и возвращается дескриптор (16-разрядное число, которое нужно просто запомнить - именно с помощью этого числа в дальнейшем программа сможет обратиться к именно этому файлу). При вызове:
- AH = 3cH
- CX = атрибут (в нашем случае всегда 0)
- DS:DX - адрес строки, содержащей имя файла
При возврате: Успешно - флаг переноса сброшен
- AX = дескриптор
Неудачно - флаг переноса установлен
- AX = код ошибки
Открытие файла
По заданному в коде ASCIIZ пути открывает файл на указанном или текущем диске или на указанном или текущем каталоге. Возвращается дескриптор. При вызове:
- AH = 3dH
- AL = режим доступа
Биты Описание
0-2 000=чтение 001=запись 010=чтение/запись * DS:DX - адрес строки, содержащей имя файла
При возврате: Успешно - флаг переноса сброшен
- AX = дескриптор
Неудачно - флаг переноса установлен
- AX = код ошибки
Закрытие файла
При вызове:
- AH = 3Eh
- BX = дескриптор
При возврате: Успешно - флаг переноса сброшен Неудачно - флаг переноса установлен
- AX = код ошибки
Чтение файла
При вызове:
- AH = 3Fh
- BX = дескриптор
- CX = число байт для чтения
- DS:DX = адрес буфера
При возврате: Успешно - флаг переноса сброшен
- AX = число переданных байтов (может быть меньше CX!)
Неудачно - флаг переноса установлен
- AX = код ошибки
Запись в файл
При вызове:
- AH = 40h
- BX = дескриптор
- CX = число байт для вывода записи
- DS:DX = адрес буфера
При возврате: Успешно - флаг переноса сброшен
- AX = число переданных байтов (может быть меньше CX!)
Неудачно - флаг переноса установлен
- AX = код ошибки