Рассылка закрыта
При закрытии подписчики были переданы в рассылку "RFpro.ru: Ассемблер? Это просто! Учимся программировать" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Уроки ассемблеру. Быстро и просто. Урок 7
Доброго времени суток, дорогие читатели! В этом уроке не будет практических занятий, лишь теория. В рамках того,
что было изучено ранее. Причём преподавать теорию будут известные мастера
своего жанра, то есть большей частью наш урок будет составлен из цитат (не зря наш урок выходит 1-го сентября). Итак, поехали. В
общем случае значения помещаются в регистры с помощью инструкции MOV.
MOV AX,BX пересылает содержимое регистра BX в AX, затирая ранее
содержащееся в AX значение. MOV AH,BL приводит к пересылке байта из
регистра в регистр, но MOV AX,BL - недопустимая инструкция, так как
значения должны иметь одинаковый размер. Инструкция MOV можеть также
передавать значения из памяти, например, MOV AX,ACCT_NUMBER. Здесь
ACCT_NUMBER - имя переменной, которую создал программист, совсем как в
языке высокого уровня. Переменная создается оператором вида ACCT_NUMBER
DW 0. Этот оператор оставляет место для слова (двух байтов), присваивая
им значение 0. Другие допустимые символы в этом операторе это DD для
двойного слова и DB - для байта или строк. Ассемблер следит за адресами
переменных, поэтому при ассемблировании оператора MOV AX,ACCT_NUMBER имя
переменной заменяется на ее адрес. Работа с именами переменных - самый простой способ идентификации данных в программах на языке ассемблера. Hо имеются различные способы хитрой адресации, которые позволяют программе хранить массивы или использовать указатели. Hапример, MOV AX,{BX][S}[SI] посылает в AX значение, которое содержится по смещению, равному сумме значений регистров BX и SI. Hо от чего отсчитывать смещение? Ответ заключается в том, что все данные собраны в одну часть программы, а весь исполняемый код - в другую. Часть, отведенная под данные, называется сегментом данных, а под программу - кодовым сегментом. Все переменные, отведенные для хранения данных, адресуются через смещение относительно начала сегмента данных. Позиция в памяти, с которой начинается сегмент данных, хранится в регистре DS, одном из четырех сегментных регистров. Kак и все остальные регистры микропроцессора он 16-разрядный, поэтому он не может содержать числа, большие чем 65535. Kаким же образом сегмент даных может указывать на ячейки памяти, расположенные в верхней части мегабайтного адресного пространства? Ответ состоит в том, что сегментные регистры автоматически умножаются на 16, а результат указывает на место в памяти, с которого начинается сегмент. Таким образом, сегменты всегда выравнены на 16-байтную границу. После того как сегмент установлен, все остальные регистры могут содержать смещения, указывающие на любой из следующих 65535 байтов. Регистр дополнительного сегмента (ES) также используется для указания на данные, хранящиеся в памяти. Разъясняет и продолжает Питер Абель, автор книги "Ассемблер и программирование для IBM PC". Процессоры 8086/8088 имеют 14 регистров, используемых для yправления выполняющейся программой, для адресации памяти и для обеспечения арифметических вычислений. Каждый регистр имеет длину в одно слово (16 бит) и адресуется по имени. Биты регистра принято нумеровать слева направо: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Процессоры 80286 и 80386 имеют ряд дополнительных регистров, некоторые из них 16-битовые. Эти регистры здесь не рассматриваются. Сегментные регистры CS, DS, SS и ES Каждый сегментный регистр обеспечивает адресацию 64К памяти, которая называется текущим сегментом. Как показано ранее, cегмент выравнен на границу параграфа и его адрес в сегментном pегистре предполагает наличие справа четырех нулевых битов. 1. Регистр CS. Регистр сегмента кода содержит начальный адрес сегмента кода. Этот адрес плюс величина смещения в командном указателе (IP) определяет адрес команды, которая должна быть выбрана для выполнения. Для обычных программ нет необходимости делать ссылки на регистр CS. 2. Регистр DS. Регистр сегмента данных содержит начальный адрес сегмента данных. Этот адрес плюс величина смещения, определенная в команде, указывают на конкретную ячейку в сегменте данных. 3. Регистр SS. Регистр сегмента стека содержит начальный адрес в сегменте стека. 4. Регистр ES. Некоторые операции над строками используют дополнительный сегментный регистр для управления адресацией памяти. В данном контексте регистр ES связан с индексным регистром DI. Если необходимо использовать регистр ES, ассемблерная программа должна его инициализировать. Регистры общего назначения: AX, BX, CX и DX При программировании на ассемблере регистры общего назначения являются "рабочими лошадками". Особенность этих регистров состоит в том, что возможна адресация их как одного целого слова или как oднобайтовой части. Левый байт является старшей частью (high), a правый - младшей частью (low). Например, двухбайтовый регистр CX состоит из двух однобайтовых: CH и CL, и ссылки на регистр возможны по любому из этих трех имен. Следующие три ассемблерные команды засылают нули в регистры CX, CH и CL, соответственно: MOV CH,00 MOV CL,00 1. Регистр AX. Регистр AX является основным сумматором и применяется для всех операций ввода-вывода, некоторых операций над строками и некоторых арифметических операций. Например, команды умножения, деления и сдвига предполагают использование регистра AX. Некоторые команды генерируют более эффективный код, если они имеют ссылки на регистр AX. 2. Регистр BX. Регистр BX является базовым регистром. Это единственный регистр общего назначения, который может использоваться в качестве "индекса" для расширенной адресации. Другое общее применение его - вычисления. 3. Регистр CX. Регистр CX является счетчиком. Он необходим для управления числом повторений циклов и для операций сдвига влево или вправо. Регистр CX используется также для вычислений. 4. Регистр DX. Регистр DX является регистром данных. Он применяется для некоторых операций ввода/вывода и тех операций умножения и деления над большими числами, которые используют регистровую пару DX и AX. Любые регистры общего назначения могут использоваться для cложения и вычитания как 8-ми, так и 16-ти битовых значений. Регистровые указатели: SP и BP Регистровые указатели SP и BP обеспечивают системе доступ к данным в сегменте стека. Реже они используются для операций сложения и вычитания. 1. Регистр SP. Указатель стека обеспечивает использование стека в памяти, позволяет временно хранить адреса и иногда данные. Этот регистр связан с регистром SS для адресации стека. 2. Регистр BP. Указатель базы облегчает доступ к параметрам: данным и адресам переданным через стек. Индексные регистры: SI и DI Оба индексных регистра возможны для расширенной адресации и для использования в операциях сложения и вычитания. 1. Регистр SI. Этот регистр является индексом источника и применяется для некоторых операций над строками. В данном контексте регистр SI связан с регистром DS. 2. Регистр DI. Этот регистр является индексом назначения и применяется также для строковых операций. В данном контексте регистр DI связан с регистром ES. Регистр командного указателя: IP Регистр IP содержит смещение на команду, которая должна быть выполнена. Обычно этот регистр в программе не используется, но он может изменять свое значение при использовании отладчика DOS DEBUG для тестирования программы. Флаговый регистр Девять из 16 битов флагового регистра являются активными и определяют текущее состояние машины и результатов выполнения. Многие арифметические команды и команды сравнения изменяют состояние флагов. Назначение флаговых битов: Флаг Назначение O (Переполнение) Указывает на переполнение старшего бита при арифметических командах. D (Направление) Обозначает левое или правое направ ление пересылки или сравнения строковых данных (данных в памяти превышающих длину одного слова). I (Прерывание) Указывает на возможность внешних прерываний. T (Пошаговый режим) Обеспечивает возможность работы процессора в пошаговом режиме. На пример, программа DOS DEBUG уста навливает данный флаг так, что воз можно пошаговое выполнение каждой команды для проверки изменения содержимого регистров и памяти. S (Знак) Содержит результирующий знак после арифметических операций (0 - плюс, 1 - минус). Z (Ноль) Показывает результат арифметичес ких операций и операций сравнения (0 - ненулевой, 1 - нулевой результат). A (Внешний перенос) Содержит перенос из 3-го бита для 8-битных данных, используется для специальных арифметических операций. P (Контроль четности) Показывает четность младших 8-битовых данных (1 - четное и 0 - нечетное число). C (Перенос) Содержит перенос из старшего бита, после арифметических операций, а также последний бит при сдвигах или циклических сдвигах. Продолжает пользователь xrnd, автор статьи "Директивы объявления данных" учебного курса сайта asmworld.ru: Практически любая программа кроме машинных команд содержит также какие-то данные. Например, числа, текстовые строчки, идентификаторы, различные ресурсы и т.д. Данные могут быть как константами, не меняющими своё значение во время выполнения программы, так и переменными, в которых хранятся всякие промежуточные результаты. Прежде всего нужно научиться объявлять данные в программе. Для этого в ассемблере существуют директивы объявления данных. В учебном курсе для нас самыми полезными будут директивы db (1 байт), dw (2 байта) и dd (4 байта). Синтаксис объявления данных Объявлять данные очень просто — например, чтобы объявить байт cо значением 5 достаточно написать: где x — название нашей переменной или константы, db — директива объявления байта, а 5 — значение. С помощью названия в программе можно будет обращаться к ячейке памяти, содержащей наш байт. Объявление последовательностей (массивов) Иногда в программе требуется объявить массив, то есть несколько переменных одинакового размера, расположенных в памяти друг за другом. Например, чтобы объявить массив из 5 двухбайтных чисел можно написать: где array1 — название массива, 1,2,3,4,5 — значения элементов. Вместо array1 компилятор masm будет подставлять в программу адрес начала массива, то есть адрес первого элемента. Для объявления повторяющихся элементов можно использовать такую запись (объявляем массив из 5 байтов, равных 1): А ещё можно вот так объявить массив (догадайтесь сами, что тут получается): Объявление строк Строка представляет собой массив байтов-символов и записывается в одинарных кавычках: Для обозначения конца строки используется специальный символ. Обычно это нулевой байт, но для функций DOS используется символ ’$’. str3 db 'Hello$' ;Для DOS Резервирование данных (точнее памяти для них) Можно объявлять переменные, не имеющие определённого начального значения. Такие переменные называются неинициализированными. Например, их можно использовать в программе для хранения временного или промежуточного значения. Фактически под переменную просто резервируется место в памяти. Объявлять такие переменные можно с помощью директив db, dw, dd, … и знака вопроса вместо значения. x2 dw ?,?,? x3 dd 10 dup(?) Директива file file — это особая директива объявления данных, которая позволяет добавить в исполняемый файл последовательность байтов из внешнего файла. Иногда это может быть очень удобно. Например, если вы хотите добавить изображение в исполняемый файл (в виде данных), или большой кусок текста, или даже код из другого файла. Директива используется следующим образом: data2 file 'data.bin':20 ;Добавить байты из файла data.bin, начиная со смещения 20. data3 file 'data.bin':20,5 ;Добавить 5 байтов из файла data.bin, начиная со смещения 20. И снова Роберт Журден. После того как программист на ассемблере установил три сегментных регистра (CS, DS и SS) и загрузил данные в регистры микропроцессора он имеет широкий набор встроенных средств, которыми процессор может помочь программисту на ассемблере. Вот наиболее распространённые из них: ;тания (SUB), а также варианты обеих этих инструкций. ; MUL BL ; Умножает BL на AX. Имеется также инструкция деления ; (DIV), а также варианты обеих этих инструкций. ; INC BL ; Увеличивает BL на 1. Имеется также инструкция умень- ; шения (DEC). ; LOOP XXX ;Возвращает программу назад к строке помеченной XXX, ; повторяя процесс столько раз, какое число содержится ; в CX (аналогично инструкции FOR .. TO .. NEXT в Бей- ; сике). ; OR AL,BL ;Выполняет операцию логического ИЛИ над содержимым ; регистров AL и BL, причем результат помещается в AL. ; Имеются также инструкции AND, XOR и NOT. ; SHL AX,1 ; Сдвигает все биты, содержащиеся в AX, на одну позицию ; влево. Это эквивалентно умножению содержимого AX на ; 2. Другие инструкции сдвигают биты вправо или осу- ; ществляют циклический сдвиг. Все эти инструкции очень ; полезны для битовых операций, таких как установка ; точек экрана. IN AL,DX ; Помещает в AX байт, обнаруженный в порте, адрес кото- ; рого указан в DX. Имеется также инструкция OUT. ; JMP ; Передает управление в другое место программы, как ; инструкция GOTO в Бейсике. JMP YYY передает управле- ; ние на строку программы, имеющую метку YYY. CMP AL,BL ; Сравнивает содержимое AL и BL. За инструкцией CMP ; обычно следует инструкция условного перехода. Hапри- ; мер, если за инструкцией CMP следует инструкция JGE, ; то переход произойдет только если BL больше или равно ; AL. Инструкция CMP достигает того же результата, что ; и инструкция IF .. THEN в Бейсике (на самом деле ; инструкция IF .. THEN переводится интерпретатором ; Бейсика в инструкцию CMP). TEST AL,BL ; Проверяет есть ли среди битов, установленных в BL, ; такие, которые установлены также и в AL. За этой ; инструкцией обычно следует команда условного перехо- ; да, так же как за CMP. TEST очень полезен при провер- ; ке статусных битов (битовые операции очень просто ; реализуются в языке ассемблера). MOVS ; Пересылает строку, длина которой содержится в CX, с ; места, на которое указывает SI, на место, на которое ; указывает DI. Имеется еще несколько других инструк- ; ций, связанных с пересылкой и поиском строк. Язык ассемблера обеспечивает несколько вариантов этих инструкций, а также ряд других специальных инструкций. Имеется также целый класс инструкций, называемых псевдооператорами, которые помещаются в текст программы с целью указания ассемблеру как обрабатывать данную программу. Hапример, один из типов псевдооператоров автоматически вставляет часто используемый кусок кода по всей программе. Такая порция кода называется макросом и именно это свойство ассемблера дало ему название "макроассемблер". И, наконец, ассемблер имеет возможность, которой завидуют (или, по крайней мере, должны завидовать) все кто программирует только на языках высокого уровня. Имеется ввиду возможность оптимальным образом использовать прерывания операционной системы. Ведь это ничто иное, как готовые процедуры. Однако вместо того, чтобы вызывать их по CALL, они вызываются инструкцией INT. INT21H вызывает прерывание с шестнадцатеричным номером 21. Имеется ряд таких прерываний, как в базовой системе ввода/вывода ПЗУ, так и в операционной системе, причем некоторые из этих процедур необычайно мощны. Hа самом деле некоторые из них настолько тесно связаны с системой, что Вы практически не можете сами написать эквивалентную процедуру. Языки высокого уровня позволяют использовать многие из этих прерываний. Они используют их для вывода на экран, приема ввода с клавиатуры и доступа к дискам. Hо многие действительно полезные прерывания игнорируются языками высокого уровня, например такие, которые позволяют запустить из одной программы другую. Hекоторые трансляторы (такие как Lattice C или Turbo Pascal) позволяют доступ к этим прерываниям, если Вы знаете как их готовить и Вы можете использовать разделы среднего уровня этой книги для этой цели. Перед
вызовом прерывания некоторая информация должна быть помещена в регистры
процессора. Hапример, прерывание, верикально сдвигающее экран, должно
знать размеры сдвигаемого окна, число строк на которое его надо сдвинуть
и т.д. Эти значения часто называют входными регистрами. Снова и снова
Вы будете встречать слова "при входе BX должен содержать ...",
описывающие спецификацию входных регистров. Аналогично, при возврате из
прерывания некоторые регистры возвращают значения или статусную
информацию. Они называются выходными регистрами и мы описываем их
словами "при выходе AX содержит ...". Зачастую одно прерывание содержит
много функций. В частности, операционная система впихнула практически
все свои возможности в прерывание 21H. Поэтому при вызове прерывания
необходимо указывать номер функции. Все прерывания (как BIOS так и DOS)
передают номер функции в AH (иногда в AL содержится номер подфункции). Какой огромный урок! Ну ничего страшного, в следующий раз мы попробуем описать гораздо более интересные вещи. Например, запись информации в файл. |
В избранное | ||