Лабораторная работа № 2
Основы использования языка Ассемблера
Цель работы Получение начальных сведений о низкоуровневом программировании центрального процессора и памяти вычислительной системы.
Указания к выполнению работы
Понятие языков ассемблера
Как известно, языки, представляющие алгоритмы в виде последовательности читаемых программистом команд, называются алгоритмическими языками и подразделяются на две большие группы: машинно-ориентированные и языки высокого уровня.
Машинно-ориентированные языки относятся к языкам программирования низкого уровня и позволяют максимально учитывать функциональные и структурные особенности конкретной ЭВМ. По этой причине программирование на таких языках наиболее трудоемко, зато созданные программы будут работать быстрее. Кроме того, машинно-ориентированные языки предоставляют специалисту полный доступ ко всем блокам ЭВМ.
Языки высокого уровня предполагают использование макрокоманд при описании алгоритмов. При этом каждая макрокоманда соответствует десяткам или даже сотням машинных команд (1 команда машинно-ориентированного языка обычно соответствует 1 машинной команде). Использовать такие языки гораздо удобнее, однако они неизбежно обладают свойством избыточности: языки высокого уровня не учитывают особенности конкретной ЭВМ.
Как машинно-ориентированные языки, так и языки высокого уровня должны быть переведены на машинный язык двоичных кодов для того, чтобы написанная программа могла быть прочитана и исполнена компьютером. Процедура перевода осуществляется при помощи специальных программ – трансляторов. Машинно-ориентированные языки транслируются довольно легко, поскольку их команды максимально соответствуют командам машинного языка. Перевод программ, написанных на языках высокого уровня – значительно более сложная задача. Набор и формат машинных команд зависит от класса и типа ЭВМ. Кроме того, практически все программы в процессе своей работы используют функции операционной системы. Поэтому программа-транслятор должна быть своя для каждого типа или класса ЭВМ и для каждой используемой ОС. Поскольку машинно-ориентированные языки максимально близко соответствуют машинному языку ЭВМ, то для каждого типа или класса ЭВМ должен существовать свой машинно-ориентированный язык. Процесс перевода с машинно-ориентированного языка в машинные команды получил название ассемблирования (от англ. assemble – собирать). Программу-транслятор, выполняющую ассемблирование, стали называть ассемблером, а сами машинно-ориентированные языки – языками ассемблера.
В отличие от языков ассемблера, программы на языках высокого уровня, могут без каких-либо существенных изменений переноситься между ЭВМ разных типов или классов. Все различия в наборе и формате машинных команд будут учтены на этапе трансляции.
Таким образом, основное достоинство машинно-ориентированных языков – эффективность и быстрота работы программ. Основное достоинство языков высокого уровня – удобство и переносимость.
Формат машинной команды
Опр. Машинная команда – это элементарная инструкция машине, которая выполняется автоматически без каких-либо дополнительных указаний и пояснений.
Машинная команда состоит из двух частей: операционной и адресной. Операционная часть команды содержит код операции машины. Адресная часть содержит коды одного или нескольких адресов ячеек памяти машины, предназначенных для оперативного хранения данных при выполнении команды. Иными словами, адресная часть предназначена для хранения адресов операндов. По количеству адресов, записываемых в команде, команды делятся на безадресные, одно-, двух- и трехадресные. Большинство команд в современных ПК относятся к двухадресным и имеют такую структуру:
КОП а1 а2
Здесь КОП – код операции, а1 – адрес ячейки памяти, где хранится первое из чисел, а2 – адрес ячейки памяти, где хранится второе из чисел. Результат исполнения такой команды помещается в ячейку а1. Ячейками памяти а1 и а2 могут быть любые ячейка основной памяти, в том числе регистры микропроцессорной памяти.
Современные ЭВМ могут выполнять сотни машинных команд, выполняющих самые разные функции:
Арифметические и логические операции на информацией;
Операции пересылки информации внутри ЭВМ и обращения к внешним устройствам;
Операции над текстом (последовательностями символов);
Операции передачи управления;
…
Понятие о системе прерываний
Опр. Прерывание – это приостановка выполнения в процессоре программы с целью выполнения какой-то более важной или нужной в данный момент другой программы или процедуры, после завершения которой продолжается выполнение прерванной программы с момента ее прерывания.
Система прерывания программ играет важную роль при реализации многопрограммных режимов работы ЭВМ.
При реализации прерывания процессор выполняет следующие действия:
При поступлении запроса на прерывание проверяется допустимость такого прерывания. Прерывание такого типа должно быть разрешено для исполнения, а его приоритет должен быть выше приоритета исполняемой программы. Приоритет прерывания зависит от его вида. Наименьшим приоритетом обладают прикладные прерывания, которые временно устанавливаются пользователем при многопрограммной работе процессора. Более высоким приоритетом обладают аппаратные прерывания, которые инициируются различными внешними устройствами – таймером, клавиатурой, принтером и т.п. Для систематизации и определения очередности выполнения при одновременном возникновении нескольких из них, используется контроллер прерываний. Существуют также программные прерывания, которые инициируются самой программой обычно для того, чтобы обратиться к служебным функциям ОС или BIOS. Наивысшим приоритетом обладают технические и логические прерывания, которые возникают при появлении отказов или сбоев в работе технических средств или при появлении логических ошибок в выполняемых программах (деление на ноль, попытка обращения к ячейке памяти с недопустимому адресом и т.п.). Прерывания последних двух видов обычно не могут быть запрещены, следовательно, вызываются всегда.
Процессор запоминает в стековой памяти текущее состояние прерываемой программы: адрес текущей команды, регистр флагов со служебной информацией и т.п.
Посылает источнику прерывания запрос о причине прерывания;
По коду запрошенного прерывания определяет адрес подпрограммы, которая должна быть вызвана для обработки прерывания.
Считывает по определенному адресу атрибуты прерывания в регистры микропроцессорной памяти.
Выполняет подпрограмму для обработки прерывания (драйвер прерывания).
Возвращает из стека параметры прерванной программы – адрес текущей команды, флаги регистра флагов, иные сохраненные атрибуты.
Восстанавливает процесс выполнения прерванной программы.
Адресация памяти в ассемблере
На самом верхнем уровне иерархии основной памяти ЭВМ стоят регистры микропроцессорной памяти (регистры МПП). Эти ячейки памяти расположены непосредственно в микропроцессоре и предназначены для хранения данных, которые используются при выполнении ближайших машинных команд. Регистры обладают максимальным быстродействием с точки зрения доступа к хранящимся в них данным. Микропроцессоры современных ЭВМ содержат следующие виды регистров:
Регистры общего назначения. К ним относятся шестнадцатиразрядные регистры AX, BX, CX, DX. К каждому из них можно обратиться в целом или к одной из двух 8-разрядных частей. Например, регистру AX соответствуют части AH (старшие 8 бит, от англ. High) и AL (младшие 8 бит, от англ. Low). Указанные регистры могут использоваться программистом для решения любых задач. Кроме того, с каждым из них связано некоторые дополнительные функции. Например, регистр CX используется как счетчик при организации циклов с заданным числом повторений (см. далее). В первых микропроцессорах семейства Intel x86 регистры общего назначения были шестнадцатиразрядными. В современных ЭВМ используются 32-разрядные регистры, причем для обращения к младшим 16 разрядам используются обозначения указанные выше (AL, AH, AX и др.), а для обращения ко всему регистру – введены дополнительные обозначения: EAX, EBX, ECX и EDX.
Регистры сегментов. К ним относятся регистры CS, SS, DS, ES. Чтобы понять их назначение, следует знать, что многие современные микропроцессоры (в частности, семейства Intel x86) аппаратно поддерживают так называемую сегментную организацию оперативной памяти ЭВМ. В соответствии с ней, вся оперативная память ЭВМ разбивается на несколько смежных блоков, каждый из которых называется сегментом. Таким образом, несмотря на то, что физически оперативная память представляет собой линейную последовательность ячеек, ее логическая организация является двумерной: первый уровень соответствует сегментам, а второй – отдельным ячейкам в некотором сегменте. Чтобы идентифицировать сегменты в оперативной памяти, каждый из них должен иметь свой адрес. В первых процессорах семейства Intel x86 адрес сегмента состоял из 20 бит и соответствовал физическому адресу своей первой ячейки памяти. Длина каждого сегмента должна была быть равна целому числу параграфов (1 параграф равен 16 байтам). Кроме того, сегмент должен был обязательно начинаться только с первой ячейки некоторго параграфа. Это позволило добиться того, что последние 4 бита адреса любого сегмента всегда равны нулю. Описанный способ адресации сегментов используется и в современных микропроцессорах Intel x86 при работе в реальном режиме (real-mode), который был специально сохранен для обеспечения совместимости «снизу-вверх».
Особенностью сегментной организации памяти является то, что каждый сегмент, с которым работает некоторая программа, имеет свое специальное назначение в этой программе:
Сегмент кода содержит команды выполняющейся команды;
Сегмент данных содержит обрабатываемые программой данные: ячейки для хранения переменных, объявленные константы и т.п.
Сегмент стека представляет собой область памяти, используемую программой в качестве стека, то есть по принципу «последний записанный элемент выбирается первым». Стек используется для сохранения контекста исполняемой программы при передаче управления в подпрограммы, организации рекурсии, а также для временного сохранения и последующего восстановления каких-либо важных данных.
Таким образом, относительно сегментной организации памяти следует запомнить, что:
Сегмент, это некоторый блок ячеек основной памяти, который соответствует первому уровню двухуровневой логической организации оперативной памяти ЭВМ.
С каждой программой связано как минимум три сегмента памяти: сегмент команд, сегмент данных и сегмент стека. Смешивать назначение указанных сегментов, то есть хранить, например, данные в сегменте команд, не рекомендуется.
Физический адрес первой ячейки сегмента соответствует адресу всего сегмента, по которому его можно обнаружить в оперативной памяти.
Поддержка сегментной организации аппаратно реализована во многих современных микропроцессорах. Тем не менее, такая организация ячеек оперативной памяти не является единственной.
Чтобы исполняющаяся программа имела возможность работать с выделенными для нее сегментами, используются упомянутые выше регистры сегментов:
Регистр CS идентифицирует сегмент кода;
Регистр DS идентифицирует сегмент данных;
Регистр SS идентифицирует сегмент стека;
Регистр ES предназначен для идентификации дополнительного сегмента данных в тех случаях, когда одного сегмента данных (DS) для программы не достаточно. В современных микропроцессорах добавлено еще два дополнительных сегмента данных: FS и GS.
Выше мы рассмотрели способ адресации сегментов при работе микропроцессоров семейства Intel x86 в реальном режиме. В 20-битном адресе сегмента последние 4 бита всегда равны нулю, поэтому в каждом из сегментных регистров достаточно хранить только первые 16 бит адреса соответствующего сегмента.
Следует помнить, что при запуске программы за корректную инициализацию регистров CS, DS и SS отвечает операционная система. Все остальные действия, связанные с изменением их значений в процессе работы программы, должны быть проверены и отслежены программистом.
Регистры-указатели. К ним относятся регистры BP, SI, DI, SP, IP. Каждый из них используется для хранения указателей на некоторые важные для данной программы ячейки памяти. Ранее было указано, что логическая организация оперативной памяти содержит два уровня: на первом уровне фиксируется сегмент, на втором – отдельная ячейка памяти внутри сегмента. Поскольку в сегменте ячейки памяти расположены линейно (последовательно друг за другом), то относительным адресом каждой ячейки можно считать ее порядковый номер в сегменте, то есть смещение относительно первой ячейки. Зная адрес сегмента и относительный адрес (смещение) ячейки, для нее можно легко получить значение абсолютного адреса по формуле:
Аабс = 16 × Асегм. + [смещ]. (*)
Здесь Асегм. – это 16-битное значение адреса сегмента, которое хранится в соответствующем сегментном регистре (CS, SS или DS). Умножение его на 16 дает 20-битный адрес сегмента, который совпадает с физическим адресом первой ячейки в этом сегменте. Наконец, [смещ] – это смещение адресуемой ячейки по отношению к началу сегмента. Абсолютный адрес, вычисляемый по формуле (*), часто записывают в форме: Асегм : [смещ]. Следует помнить, что при работе микропроцессоров семейства Intel x86 в реальном режиме относительный адрес (смещение) ячейки состоит из 16 бит. Таким образом, максимальный размер 1 сегмента при работе в реальном режиме составляет 216 = 65 536 байт = 64 Кб.
Регистры-указатели используются для хранения относительных адресов тех или иных объектов программы. Особое значение имеют регистры IP и SP, которые используются в паре соответственно с регистром CS и SS. Регистр IP называется регистром счетчика команд и содержит относительный адрес очередной исполняемой команды. Регистр SP определяет текущее смещение вершины стека. Регистры BP, SI и DI обычно используются в паре с сегментами данных DS или ES.
Следует иметь в виду, что в современных микропроцессорах сегментные регистры и регистры-указатели являются 32-разрядными, однако, как уже говорилось, при работе в реальном режиме, программист имеет доступ только к младшим 16 битам – так как это было в первых процессорах семейства Intel x86.
-
Регистр флагов – это специальный регистр, в котором каждый бит определяет наличие или отсутствие некоторого условия (флага), связанного с результатом выполнения последней команды, либо текущее состояние микропроцессора. Рассмотрим назначение некоторых битов в регистре флагов:
CF (0-ой бит) – признак переноса. Устанавливается равным 1, если в результате последней выполненной операции имеет место перенос или заем из старшего бита. В противном случае – равен 0. Флаг переноса используется для выполнения операций над числами, длиной в несколько слов.
ZF (6-ой бит) – признак нуля. Устанавливается равным 1, если результат последней выполненной операции равен нулю. В противном случае – равен 0.
SF (7-ой бит) – признак знака. Равен 1, когда старший бит результата последней выполненной операции равен 1. В противном случае – равен 0.
OF (11-ый бит) – признак переполнения. Равен 1, когда возникает переполнение, то есть объем результата последней выполненной операции превышает размер ячейки для его хранения. В противном случае – равен 0.
На нижних уровнях иерархии по отношению к памяти микропроцессора стоят ячейки основной памяти. С точки зрения программной модели, доступ ко всем ячейкам осуществляется единообразно, не зависимо от того, к какому уровню физически относится та или иная ячейка (для сокращения времени обращения к ячейкам памяти организуются буферы памяти с более высоким быстродействиям для временного хранения данных – кэш-память 1-го и 2-го уровней). Правила адресации ячеек основной памяти при работе микропроцессора в реальном режиме были рассмотрены нами выше (см. правило (*)).
Использование ассемблерных вставок в программах на языке C.
Практически все современные языки высокого уровня предоставляют программисту возможность написания части программных процедур в виде машинно-ориентированных инструкций. Это позволяет написать «критичные по времени выполнения» участки кода наиболее оптимальным образом. Тем самым будет достигнут высокий уровень эффективности выполнения программы. С другой стороны, полностью переводить свою программу на язык ассемблера не требуется.
В среде программирования Borland C++ 3.1 инструкции языка ассемблера могут быть добавлены в текст программы с использованием ключевого слова asm. Например:
void main()
{
int i;
…
asm mov i, 5; // инструкция пересылки в переменную i значения 5
i = i + 1;
…
}
Инструкция на языке ассемблера должна следовать сразу за ключевым словом asm. Если необходимо вставить несколько ассемблерных инструкций, то соответствующую вставку можно оформить в виде блока:
void main()
{
int i;
…
asm {
// начало ассемблерной вставки. Открывающая скобка блока – на одной строчке
с ключевым словом asm.
mov i, 5; // пересылка в переменную i значения 5
inc i // увеличение значения переменной на 1
mov AX, i // пересылка значения переменной i в регистр AX
// конец ассемблерного блока
}
…
}
При написании ассемблерных вставок программист может использовать все ранее введенные имена переменных для обращения к соответствующим ячейкам памяти. Кроме того, ему становятся доступны ячейки регистров микропроцессорной памяти, которые были рассмотрены выше (регистры общего назначения, сегментные регистры, регистры указатели).
Основные инструкции языка ассемблера
Рассмотрим основные инструкции, которые можно использовать для написания ассемблерных вставок в среде Borland C++ 3.1. Следует помнить, что большинство команд языка ассемблера – двухадресные (см. ранее).
Назначение
|
Синтаксис
|
Описание
|
Команда пересылки данных
|
mov dst, src
|
Пересылает 1 байт или 1 слово из источника (src) в приемник (dst). Приемником может быть регистр МПП, а также ячейка или слово основной памяти. Источником может быть регистр МПП, ячейка или слово памяти, а также непосредственное значение (константа). Разрядности операндов должны совпадать:
mov AX, BX // корректно
mov AL, AH // корректно
mov CH, BX // некорректно
Пересылать данные между ячейками памяти (минуя регистры) командой mov нельзя. В качестве источника может стоять несложное выражение, например: mov AX, (152+FF)/10.
При пересылке данных может использоваться прямая или косвенная адресация. Для прямой адресации адрес операнда указывается символьным обозначением регистра или переменной:
mov AX, BX
mov AX, UserVar
При косвенной адресации адрес операнда хранится в указанном регистре или ячейках памяти:
mov AX, [CS] // в регистр AX
// будет помещено
// значение первых двух
// ячеек памяти сегмента
// команд
mov AL, [DS+2] // в регистр AL
// будет помещено
// значение третьего байта
// сегмента данных
|
Команда занесения данных в стек
|
push src
|
Помещает на вершину стека содержимое src – 16-разрядного значения регистра или значение двух соседних ячеек памяти, если src – адрес первой ячейки или двухбайтная переменная. Значение источника и регистра флагов не изменяются.
|
Команда извлечения слова из стека
|
pop dst
|
Извлекает слова (2 байта) с вершины стека и помещает его в dst. – любой 16-битовый регистр или две ячейки памяти. Значения регистра флагов при этом не меняется. Команды push и pop работают со стеком, который в сегменте стека программы. Значение указателя SP вершины стека при выполнении этих команд изменяется автоматически.
|
Команды сложения, вычитания и сравнения
|
add dst, src
sub dst, src
cmp dst, src
|
add – сложение двоичных чисел, sub – вычитание, cmp – сравнение, которое эквивалентно вычитанию, но значение операнда dst в данном случае не изменяется (операция влияет только на регистр флагов). Операнды dst и src должны иметь одинаковый формат (байт или слово). По результату исполнения операции устанавливаются значения флагов.
|
Команды приращения
|
inc dst
dec dst
|
Одноадресные команды, соответственно добавляющие и вычитающие 1 из значения операнда. По результату исполнения операции устанавливаются значения флагов.
|
Логические команды
|
and dst, src
or dst, src
xor dst, src
test dst, src
|
Каждая из команд выполняет побитовые операции над операндами, которые должны иметь одинаковый формат (байт или слово). and – логическое умножение («И»), or – логическое сложение («ИЛИ»), xor – сумма по модулю 2 (исключающее «ИЛИ»). По результату исполнения операции устанавливаются значения флагов. Пример: xor AX, AX – обнуление регистра AX.
Команда test эквивалентна команде xor, однако значение операнда dst в этом случае не изменяется (а лишь изменяется регистр флагов).
|
Команда безусловной передачи управления
|
jmp opr
|
Управление в программе передается по адресу операнда opr. Операнд opr может быть задан прямым или косвенным адресом. Переход по прямому адресу реализуется с использованием меток. Ассемблерные вставки в Borland С++ 3.1 не поддерживают добавление меток, поэтому их следует размещать вне вставок в обычном формате языка С++.
Пример:
…
int i;
asm {
push CS;
mov AX, i;
sub AX, 3;
jmp L;
}
int j,k
L:
asm pop CS;
…
При косвенной адресации в качестве операнда указывается адрес следующей команды, с которой будет продолжено исполнение программы.
|
Команды условной передачи управления
|
j* opr
|
В языке ассемблера существует целое семейство команд условной передачи управления, которые проверяют тот или иной разряд регистра флагов и если результат проверки – «истина», то переходят по адресу, указанному операндом opr. Рассмотрим некоторые виды команд условной передачи управления:
JE/JZ – переход, если нуль (ZF=1);
JNE/JNZ – переход, если не нуль (ZF=0);
JS – переход, если есть знак (SF=1);
JNS – переход, если нет знака (SF=0);
JC – переход, если есть перенос (CF=1);
JC – переход, если нет перенося (CF=0);
JO – переход, если есть переполнение (OF=1);
JNO – переход, если нет переполнения (OF=0).
Формат операнда opr такой же, как для команды jmp. Ниже приведен пример функции, которая прибавляет к числу 1, если оно меньше 5 и отнимает – в противном случае:
int asmfunc(i: integer)
{
int r;
asm {
mov AX, i;
cmp AX, 5;
jc L1; // если AX < 5, то установится флаг переноса
dec AX;
jmp L2;
}
L1:
asm inc AX;
L2:
asm mov r, AX;
}
|
Команда управления циклами
|
loop L
|
Команда loop используется для повторения цикла заданное число раз. Счетчик повторений находится в регистре CX. При выполнении команды происходит проверка значения счетчика. Если оно больше нуля, то выполняется переход к началу цикла по метке L. При этом значение счетчика CX автоматически уменьшается на 1. При обнулении счетчика выполнение цикла завершается.
Пример:
…
asm mov CX, 100 // инициализация счетчика
L:
asm {
// тело цикла
loop L
}
…
|
Команда вызова прерывания
|
int opr
|
Вызывает прерывание исполняемой программы и передает управление процедуре обработке прерываний с кодом, указанным операндом opr. Особый интерес представляют собой системные прерывания базовой системы ввода/вывода (BIOS) ЭВМ и прерывания ОС, которые позволяют программам на языке ассемблера осуществлять взаимодействие с такими устройствами ввода/вывода как дисплей, клавиатура или принтер. При написании ассемблерных вставок вызов таких прерываний практически не используется (ввод/вывод информации реализуется средствами языков высокого уровня). Тем не менее, при изучении инструкций ассемблера, получение навыков работы с системными прерываниями является полезным.
Прерывание BIOS для работы с дисплеем имеет шестнадцатеричный код 10h. В зависимости от значения регистра AH оно обеспечивает 16 процедур работы с дисплеем. Например:
AH = 6, AL = 0 – очистка дисплея.
AH = 2 – перемещение курсора в заданную позицию. Координаты курсора (строка, столбец) должны быть записаны в регистры DH и DL перед вызовом прерывания.
Прерывание DOS для работы дисплеем и клавиатурой имеют шестнадцатеричный код 21h. В зависимости от значения регистра AH оно обеспечивает различные процедуры, например:
AH = 2 – вывод символа на экран дисплея. ASCII-код символа должен храниться в регистре DL перед вызовом прерывания.
AH = 9 – вывод строки символов. При вызове прерывания в регистрах DS:DX должен храниться адрес начала строки (DS – адрес сегмента, DX – адрес смещения в сегменте). Строка символов должна заканчиваться символом '$’.
AH = 1 – чтение символа с клавиатуры с ожиданием ввода и отображением символа на экране дисплея. После завершения обработки прерывания ASCII-код считанного символа помещается в регистр AL.
AH = 7 – чтение символа с клавиатуры с ожиданием ввода без отображения символа на экране дисплея. После завершения обработки прерывания ASCII-код считанного символа помещается в регистр AL.
Пример (очистка дисплея средствами языка ассемблера):
…
asm {
xor AX, AX; // обнуление регистра AX
mov AH, 6; // код процедуры очистки дисплея
int 10h; // вызов прерывания BIOS
}
…
|
Пример использования ассемблерных вставок
char *asmfunc(char *S, char c)
{
char *r = 0;
L0:
asm {
mov SI,S;
mov DL,c;
}
L1:
asm {
mov AL,0;
add AL,[SI];
je L3;
xor AL,DL;
je L2;
add SI,1;
jmp L1;
}
L2:
asm mov r,SI;
L3:
return r;
}
В данной работе мы рассматриваем лишь самые основные инструкции языка ассемблера. Для более глубокого ознакомления с машинно-ориентированными языками следует использовать специальные учебные пособия и справочники. Следует иметь в виду, что современные языки ассемблера предлагают программисту довольно мощные средства для разработки и оптимизации приложений, включая, например, объектно-ориентированный подход или инструменты для быстрой разработки приложений.
Задание к лабораторной работе
Внимательно изучить текст функции asmfunc, приведенной в указаниях к лабораторной работе и определить ее назначение.
Написать функцию на языке С++, которая выполняет действия, аналогичные функции asmfunc.
Сравнить быстродействие функций asmfunc и аналогичной функции на языке C++. Для этого замерить время выполнения каждой функции, управляя значениями следующих параметров: 1) длина строки S; 2) количество последовательных повторных вызовов каждой функции. Сделать выводы по проведенному исследованию.
-
С использованием технологии ассемблерных вставок реализовать функцию по заданию своего варианта. Требования к функции и к программе в целом:
Ввод данных реализовывать средствами языка С++.
Вывод информации на экран реализовывать средствами языка ассемблера, если иное не указано в задании. Для упрощения алгоритма вывода на экран числовой информации использовать шестнадцатеричную систему счисления. Рекомендуется организовать алгоритм вывода чисел в отдельной функции.
В начале работы программы средствами языка ассемблера предлагать пользователю очистить экран и очищать его при необходимости.
Не завершать работу программы до тех пор, пока пользователь не нажмет клавишу на клавиатуре. Чтение символа с клавиатуры реализовать командами языка ассемблера.
В отчете к лабораторной работе отразить ход выполнения работы, привести текст соответствующих программных процедур, изложить сделанные выводы.
Варианты заданий
Вывод на экран строки символов. Входные параметры функции: char *S – строка для вывода на экран, char tc – символ, обозначающий конец строки, int sl – флаг вывода длины. Если sl <> 0, то после вывода строки выводить на экран ее длину.
Проверка неравенства треугольника. Входные параметры функции: int a,b, c - длины сторон треугольника. Если составить треугольник можно, то возвращать 0. В противном случае возвращать -1 и выводить на экран нарушенное неравенство в форме:
[Длина стороны 1] «+»[Длина стороны 2] «<�»[Длина стороны 3].
Выбор из трех чисел двух таких, разность которых будет максимальной. Входные параметры функции: int a, b, c – произвольные числа. Форма вывода результата:
[Число 1] «пробел»[Число 2]
Нахождение N-го члена арифметической прогрессии. Входные параметры функции: int A – первый член прогрессии, int b – разность прогрессии, int N – номер искомого члена. Вывести на экран результат работы функции.
Нахождение в заданной строке символа с наибольшим ASCII-кодом. Входные параметры функции: char *S – строка для поиска, int n – длина строки. Вывести на экран найденный символ и его ASCII-код.
Подсчет в заданной строке количества вхождений некоторого символа. Входные параметры функции: char *S – строка, int n – длина строки, char c – искомый символ. Вывести на экран количество найденных символов.
Нахождение суммы максимального и минимального элементов в массиве чисел. Входные параметры функции: int A – массив чисел, int n – длина массива. Вывести на экран результат работы функции.
Отсортировать по возрастанию заданный массив чисел. Входные параметры функции: int *A – массив чисел, int n – длина массива. Вывод информации в основной программе осуществлять средствами языка C++.
Контрольные вопросы
Понятие машинно-ориентированных языков.
Достоинства и недостатки языков высокого уровня и языков ассемблера.
Регистры общего назначения МПП.
Сегментные регистры МПП.
Регистры-указатели МПП.
Регистр флагов МПП.
Адресация памяти при работе микропроцессора в реальном режиме.
Понятие и виды прерываний.
Прямая и косвенная адресация операндов в инструкциях языка ассемблера.
Команды передачи управления в языке ассемблера.
Способы задания циклов в языке ассемблера.
Какое максимальное количество оперативной памяти доступно процессорам семейства Intel x86 при работе в реальном режиме?
|