Материал: jourdain_spravochnik_programmista

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

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

кода, когда нельзя использовать процедуру ON PEN GOSUB, напишите

PEN STOP. В этом случае будет продолжаться проверка статуса пера,

и если перо будет включено, то этот факт будет запомнен. Однако

пока не будет встречен оператор PEN ON, управление не будет пере-

даваться процедуре ON PEN GOSUB.

Данный пример вызывает остановку программы, когда нажата кноп-

ка на световом пере. Точка в позиции светового пера включается

процедурой, обрабатывающей включение

ERROR ;на обработку ошибки

INC BX ;увеличиваем указатель

LOOP NEXT_CHAR ;выводим следующий символ

Стандартное прерывание MS DOS для вывода на принтер это функ-

ция 5 прерывания 21H. Просто поместите символ в DL и выполните

прерывание. Эта функция всегда выводит на LPT1 и у нее нет возв-

ращаемых регистров.

;---вывод данных на LPT1

MOV AH,5 ;номер функции

MOV DL,CHAR ;готовим печатаемый символ

INT 21H ;посылаем его на пр

;N,1)*2^(N-1)

2040 NEXT

2050 RETURN

Приложение В. Основные сведения об языке ассемблера.

Читатель этой книги, не знакомый с языком ассемблера, скоро

поймет, что многие программистские трюки не могут быть достигнуты

другими средствами. Хотя изучение языка ассемблера требует от-

дельной книги, в этом приложении приводятся основные понятия,

которые помогут новичкам разобраться в примерах на этом языке.

Внимательный просмотр разделов, посвященных среднему и низкому

уровням, даст Вам возможность получить представление о том, как

работает ассемблер, после чего намного легче изучить разные част-

ные вопросы. Здесь обсуждаются не все ассемблерные инструкции,

встречающиеся в программах, но Вы обнаружите, что около 95 %

инструкций, встреченных Вами в программах, описаны здесь, а зна-

чение остальных может быть понято благодаря комментариям к прог-

раммам.

Микропроцессор 8088 имеет 13 16-разрядных регистров, каждый из

которых имеет свои функции. В то время как в языках высокого

уровня Вы можете поместить два числа в переменные, а затем сло-

жить эти переменные, то в языке ассемблера эти числа помещаются в

регистры микропроцессора, а затем складываются значения, содержа-

щиеся в регистрах. Все операции в языке ассемблера состоят в

обмене данных с регистрами, а затем выполнении операций на ре-

гистрах, таких как изменение отдельных битов, выполнение арифме-

тических операций и т.д. Одной из причин высокой эффективности

языка ассемблера является хранение данных в регистрах микропро-

цессора; компиляторы имеют тенденцию возвращать все значения в

память после выполнения операции, а доступ к памяти требует боль-

шого времени. На рис. В-1 показаны 13 регистров микропроцессоров

8088 и 80286 (последний имеет дополнительные средства для много-

задачной работы, которые мы не будем рассматривать здесь).

Регистры AX, BX, CX и DX являются регистрами общего назначе-

ния. Их особенность состоит в том, что операции могут произво-

диться не только над содержимым всего регистра, но также и над

половиной. Каждый из четырех регистров делится на старшую и млад-

шую части, например, AH обозначает старшую половину регистра AX,

а AL - младшую. Точно так же ассемблерная программа может иметь

доступ к BH, BL, CH, CL, DH и DL. Это свойство очень полезно,

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

нами. Регистры BP, SI и DI также достаточно удобны, хотя они

могут принимать только 16-битные значения. Каждый бит регистра

флагов сообщает о соответствующем статусе процессора, например, о

том, что при выполнении арифметической операции был перенос за

разрядную сетку.

В общем случае значения помещаются в регистры с помощью инст-

рукции 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 имя переменной заменяется на ее адрес.

Работа с именами переменных - самый простой способ идентифика-

ции данных в программах на языке ассемблера. Но имеются различные

способы хитрой адресации, которые позволяют программе хранить

массивы или использовать указатели. Например, MOV AX,[BX][SI]

посылает в AX значение, которое содержится по смещению, равному

сумме значений регистров BX и SI. Но от чего отсчитывать смеще-

ние? Ответ заключается в том, что все данные собраны в одну часть

программы, а весь исполняемый код - в другую. Часть, отведенная

под данные, называется сегментом данных, а под программу - кодо-

вым сегментом. Все переменные, отведенные для хранения данных,

адресуются через смещение относительно начала сегмента данных.

Позиция в памяти, с которой начинается сегмент данных, хранит-

ся в регистре DS, одном из четырех сегментных регистров. Как и

все остальные регистры микропроцессора он 16-разрядный, поэтому

он не может содержать числа, большие чем 65535. Каким же образом

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

верхней части мегабайтного адресного пространства? Ответ состоит

в том, что сегментные регистры автоматически умножаются на 16, а

результат указывает на место в памяти, с которого начинается

сегмент. Таким образом, сегменты всегда выравнены на 16-байтную

границу. После того как сегмент установлен, все остальные регист-

ры могут содержать смещения, указывающие на любой из следующих

65535 байтов. Регистр дополнительного сегмента (ES) также исполь-

зуется для указания на данные, хранящиеся в памяти.

Среди ассемблерных инструкций, которые Вы часто будете встре-

чать в этой книге, есть инструкции загрузки сегментных и относи-

тельных адресов переменных. MOV AX,SEG ACCT_NUMBER помещает зна-

чение сегментного регистра, в котором расположен ACCT_NUMBER в

AX, а впоследствии это значение будет переслано в DS. MOV BX,OFF-

SET ACCT_NUMBER помещает в BX смещение переменной ACCT_NUMBER в

сегменте данных. После выполнения этих операций DS:BX будут ука-

зывать на ACCT_NUMBER. Если ACCT_NUMBER является одномерным мас-

сивом, то для указания на определенный элемент массива может

использоваться добавочное смещение. Вы часто будете встречать

также инструкцию LEA, предоставляющую другой способ загрузки

смещения.

Кодовый сегмент содержит последовательность машинных инструк-

ций, составляющих программу. Например, инструкция MOV существует

в виде нескольких байтов машинного кода, значение байтов которого

определяет в какой регистр идет пересылка и откуда. Регистр IP

(счетчик команд) содержит величину смещения, которая указывает на

ту инструкцию в кодовом сегменте, которая сейчас должна выпол-

няться. После выполнения инструкции IP увеличивается таким обра-

зом, чтобы он указывал на следующую инструкцию. В простейшей

программе счетчик команд будет передвигаться от первого байта

кодового сегмента к последнему, где программа и завершится. Но,

как и другие программы, программа на языке ассемблера может быть

разбита на процедуры (подпрограммы), поэтому счетчик команд может

прыгать из одного места кодового сегмента в другое.

Когда счетчик команд прыгает в другое место кодового сегмента,

то его старое значение должно быть запомнено, с тем чтобы можно

было вернуться в нужное место, так как это делает оператор RETURN

в Бейсике, возвращая управление в то место, откуда была вызвана

процедура. В языке ассемблера процедуре присваивается имя, напр-

имер, COMBINE_DATA, и оператор CALL COMBINE_DATA передает управ-

ление в процедуру. Процедура завершается инструкцией RET (возв-

рат). При вызове процедуры процессор запоминает текущее значение

счетчика команд, заталкивая его на стек.

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

После завершения процедуры старое значение счетчика команд берет-

ся из стека и выполнение программы продолжается. Стек также со-

держится в отдельном сегменте, который, совершенно естественно,

называется сегментом стека. Ему соответствует сегментный регистр

SS. В регистре SP хранится указатель стека, который всегда указы-

вает на вершину стека и изменяется при засылке на стек и выборке

из стека.

На первый взгляд стек кажется достаточно неуклюжим способом

хранения информации, но у него есть два преимущества. Во-первых,

доступ к его содержимому намного быстрее, чем к переменным, хра-

нящимся в памяти, а, во-вторых, стек может использоваться для

многих целей. Он может хранить адреса возврата из процедуры,

вложенной в другую процедуру. Впоследствии, то же самое прост-

ранство может использоваться программистом для хранения данных,

которые должны сейчас обрабатываться, но для которых не хватает

места в регистрах микропроцессора. Программа выталкивает содержи-

мое регистра на стек командой PUSH, а позднее забирает его оттуда

командой POP. В ассемблерных программах, приведенных в этой кни-

ге, Вы не раз встретитесь с инструкциями типа PUSH BX и POP DX.

Неправильный порядок обмена данными со стеком - лучший способ

привести ассемблерную программу к краху.

После того как программист на ассемблере установил три сег-

ментных регистра (CS, DS и SS) и загрузил данные в регистры мик-

ропроцессора он имеет широкий набор встроенных средств, которыми

процессор может помочь программисту на ассемблере. Вот наиболее

распространенные из них:

ADD AX,BX Прибавляет BX к AX. Существует также инструкция вычи-

тания (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

обычно следует инструкция условного перехода. Напри-

мер, если за инструкцией CMP следует инструкция JGE,

то переход произойдет только если BL больше или равно

AL. Инструкция CMP достигает того же результата, что

и инструкция IF .. THEN в Бейсике (на самом деле

инструкция IF .. THEN переводится интерпретатором

Бейсика в инструкцию CMP).

TEST AL,BL Проверяет есть ли среди битов, установленных в BL,

такие, которые установлены также и в AL. За этой

инструкцией обычно следует команда условного перехо-

да, так же как за CMP. TEST очень полезен при провер-

ке статусных битов (битовые операции очень просто

реализуются в языке ассемблера).

MOVS Пересылает строку, длина которой содержится в CX, с

места, на которое указывает SI, на место, на которое

указывает DI. Имеется еще несколько других инструк-

ций, связанных с пересылкой и поиском строк.

Язык ассемблера обеспечивает несколько вариантов этих инструкций,

а также ряд других специальных инструкций. Имеется также целый

класс инструкций, называемых псевдооператорами, которые помещают-

ся в текст программы с целью указания ассемблеру как обрабатывать

данную программу. Например, один из типов псевдооператоров авто-

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

рамме. Такая порция кода называется макросом и именно это свойст-

во ассемблера дало ему название "макроассемблер".

И, наконец, ассемблер имеет возможность, которой завидуют

(или, по крайней мере, должны завидовать) все кто программирует

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

мальным образом использовать прерывания операционной системы.

Ведь это ничто иное, как готовые процедуры. Однако вместо того,

чтобы вызывать их по CALL, они вызываются инструкцией INT. INT21H

вызывает прерывание с шестнадцатиричным номером 21. Имеется ряд

таких прерываний, как в базовой системе ввода/вывода ПЗУ, так и в

операционной системе, причем некоторые из этих процедур необычай-

но мощны. На самом деле некоторые из них настолько тесно связаны

с системой, что Вы практически не можете сами написать эквива-

лентную процедуру. Языки высокого уровня позволяют использовать

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

приема ввода с клавиатуры и доступа к дискам. Но многие действи-

тельно полезные прерывания игнорируются языками высокого уровня,

например такие, которые позволяют запустить из одной программы

другую. Некоторые трансляторы (такие как Lattice C или Turbo

Pascal) позволяют доступ к этим прерываниям, если Вы знаете как

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

книги для этой цели.

Перед вызовом прерывания некоторая информация должна быть

помещена в регистры процессора. Например, прерывание, верикально

сдвигающее экран, должно знать размеры сдвигаемого окна, число

строк на которое его надо сдвинуть и т.д. Эти значения часто

называют входными регистрами. Снова и снова Вы будете встречать

слова "при входе BX должен содержать ...", описывающие специфика-

цию входных регистров. Аналогично, при возврате из прерывания

Источник: https://files.student-it.ru/previewfile/9950