Вы находитесь на странице: 1из 96

Arhitectura calculatoarelor (lucrări de laborator ) 1

Indicaţii metodice pentru lucrări de laborator


disciplina “Arhitectura Calculatoarelor”
(limba rusă)
Arhitectura calculatoarelor (lucrări de laborator ) 2

ЛАБОРАТОРНАЯ РАБОТА №1

Разработка ассемблерных программ в Visual Studio.

1.1. Цель работы: Изучение программной модели микропроцессоров, 16-ти, 32 и 64 битных


регистров, ознакомление со средой разработки Microsoft Visual Studio.
1.2. Программная модель микропроцессоров Intel IA-32.
Любая выполняющаяся программа получает в свое распоряжение
определенный набор ресурсов процессора. Эти ресурсы необходимы для
обработки и хранения в памяти команд и данных программы, а также
информации о текущем состоянии программы и процессора.
Процессоры могут работать в различных режимах, определяющих возможности
адресации памяти и защиты: в реальном (16-разрядном) режиме процессора 8086, в режиме
виртуального процессора 8086 (V86), в защищенном 32-разрядном (и защищенном 16-
разрядном) режиме. Режим работы процессора задается операционной системой с учетом
режима работы приложений (задач, task). У процессоров с 64-битным расширением появляются
новые режимы, среди которых есть и режимы, обеспечивающие совместимость с 32-
разрядными операционными системами и приложениями. Новые режимы используются только
в 64-битных операционных системах (ОС), а полностью их преимущества доступны только 64-
битным приложениям.
Режимы 64-битных ОС:
 64-битный режим (64-bit mode) — это режим полной поддержки 64-битной виртуальной
адресации и 64-битных расширений регистров. В этом режиме используется только
плоская модель памяти (общий сегмент для кода, данных и стека). По умолчанию
разрядность адреса составляет 64 бита, а операндов (для большинства инструкций) — 32
бита, однако префиксом (REX) можно задать 64-битные операнды. Имеется новый способ
адресации данных — относительно указателя инструкций. Режим предназначен для
использования 64-битными ОС при запуске 64-битных приложений — он включается
операционной системой для сегмента кода конкретной задачи;
 режим совместимости (compatibility mode) позволяет 64-битным ОС работать с 32- и 16-
битными приложениями. Для приложений процессор выглядит как обычный 32-битный со
всеми атрибутами защищенного режима, сегментацией и страничной трансляцией. 64-
битные свойства используются только операционной системой, что отражается в
процедурах трансляции адресов, обработки исключений и прерываний. Режим включается
операционной системой для сегмента кода конкретной задачи.

64-битные ОС Windows не подерживают реальный (16-разрядный) режим работы


процессора. В этом случае необходимо использовать приложение DosBox.
Наиболее эффективно процессор работает с операндами, расположенными в его регистрах.
Состав 64 –битных регистров, с которыми работают прикладные программы, приведен на рис.
1.1.
Arhitectura calculatoarelor (lucrări de laborator ) 3
Рисунок 1.1

На рисунке 1.1 представлены регистры которые будут использованы в лабораторных работах.


Названия 64-битных регистров начинаются с буквы R. К любому из 16 общих регистров можно
обращаться как к 64-, 32-, 16- или 8-битному регистру. Старшие части этих регистров как
самостоятельные объекты недоступны.
Программисту доступны и другие регистры которые присутствуют в 64-битной програмной
модели микропроцессора.
Существуют понятия разрядности адреса и разрядности данных. Разрядность адреса
определяет, сколько битов (16, 32 или 64) используется в регистрах, формирующих адрес
данных или инструкций, расположенных в памяти. Каждому режиму работы процессоров
соответствуют своя разрядность, применяемая по умолчанию.

1.3 Реальный (16-разрядный) режим работы микропроцессора


Режим реальной адресации (real address mode), или просто реальный режим (real mode),
полностью совместим с микропроцессором 8086. В этом режиме возможна адресация до 1
Мбайт физической памяти.
Регистры реального режима (микропроцессора i8086), представлена на рис. 1.2. Все
регистры программно доступны. Он содержит двенадцать 16-разрядных программно-
адресуемых регистров, которые принято объединять в три группы: регистры данных, регистры-
указатели и сегментные регистры. Кроме того, в состав процессора входят счетчик команд и
регистр флагов (рис. 1.2). Регистры данных и регистры-указатели часто называют регистрами
общего назначения.

Рисунок 1.2 – 16-битные регистры микропроцессора

В группу регистров данных включаются регистры АХ, ВХ, СХ и DX. Программист может
использовать их по своему усмотрению для временного хранения любых объектов (данных или
адресов) и выполнения над ними требуемых операций. При этом регистры допускают
независимое обращение к старшим (АН, ВН, СН и DH) и младшим (AL, BL, CL и DL)
половинам.
Так, команда - mov BL,AH, пересылает старший байт регистра АХ в младший байт регистра
ВХ, не затрагивая при этом вторых байтов этих регистров. Еще раз отметим, что сначала
указывается операнд-приемник, а после запятой - операнд-источник, т. е. команда, выполняется
как бы справа налево. В качестве средства временного хранения данных все регистры общего
назначения (да и все остальные, кроме сегментных и указателя стека) вполне эквивалентны,
однако многие команды требуют для своего выполнения использования вполне определенных
регистров.
Например, команда умножения mul требует, чтобы один из сомножителей был в регистре
АХ (или AL), а команда организации цикла loop выполняет циклический переход СХ раз.
Arhitectura calculatoarelor (lucrări de laborator ) 4
Индексные регистры SI и DI так же, как и регистры данных, могут использоваться
произвольным образом. Однако их основное назначение - хранить индексы (смещения)
относительно некоторой базы (т. е. начала массива) при выборке операндов из памяти. Адрес
базы при этом обычно находится в одном из базовых регистров (ВХ или ВР). Примеры такого
рода будут приведены ниже.
Регистр ВР служит указателем базы при работе с данными в стековых структурах, о чем
будет речь впереди, но может использоваться и произвольным образом в большинстве
арифметических и логических операций или просто для временного хранения каких-либо
данных.
Последний из регистров-указателей, указатель стека SP, стоит особняком от других в том
отношении, что используется исключительно как указатель вершины стека.
Регистры SI, DI, ВР и SP, в отличие от регистров данных, не допускают побайтовую
адресацию.
Четыре сегментных регистра CS, DS, ES и SS хранят начальные адреса сегментов
программы и, тем самым, обеспечивают возможность обращения к этим сегментам.
Регистр CS обеспечивает адресацию к сегменту, в котором находятся программные коды,
регистры DS и ES - к сегментам с данными (таким образом, в любой точке программа может
иметь доступ к двум сегментам данных, основному и дополнительному), а регистр SS - к
сегменту стека.
Сегментные регистры, естественно, не могут выступать в качестве регистров общего
назначения.
Указатель команд IP "следит" за ходом выполнения программы, указывая в каждый
момент относительный адрес команды, следующей за исполняемой. Регистр IP программно
недоступен (IP - это просто его сокращенное название, а не мнемоническое обозначение,
используемое в языке программирования); наращивание адреса в нем выполняет
микропроцессор, учитывая при этом длину текущей команды.
Регистр флагов, эквивалентный регистру состояния микропроцессор других
вычислительных систем, содержит информацию о текущем состоянии процессора (рис. 1.3). Он
включает 6 флагов состояния и 3 бита управления состоянием процессора, которые тоже
обычно называются флагами.

Рисунок 1.3 - Регистр флагов. Дугами выделены четверки битов

Флаг переноса CF (Carry Flag) индицирует перенос или заем при выполнении
арифметических операций, а также (что для прикладного программиста гораздо важнее!)
служит индикатором ошибки при обращении к системным функциям.
Флаг паритета PF (Parity Flag) устанавливается в 1, если младшие 8 бит результата
операции содержат четное число двоичных единиц.
Флаг вспомогательного переноса AF (Auxiliary Flag) используется в операциях над
упакованными двоично-десятичными числами. Он индицирует перенос в старшую тетраду
(четверку битов) или заем из старшей тетрады.
Флаг нуля ZF (Zero Flag) устанавливается в 1, если результат операции равен нулю.
Флаг знака SF (Sign Flag) показывает знак результата операции, устанавливаясь в 1 при
отрицательном результате.
Флаг переполнения OF (Overflow Flag) фиксирует переполнение, т. е. выход результата
операции за пределы допустимого для данного процессора диапазона значений.
Флаги состояния автоматически устанавливаются процессором после выполнения
каждой команды. Так, если в регистре АХ содержится число 1, то после выполнения команды
декремента (уменьшения на единицу) -- dec AX, содержимое АХ станет равно нулю и
процессор сразу отметит этот факт, установив в регистре флагов бит ZF (флаг нуля). Если
попытаться сложить два больших числа, например, 58 000 и 61 000, то установится флаг
Arhitectura calculatoarelor (lucrări de laborator ) 5
переноса CF, так как число 119000, получающееся в результате сложения, должно занять
больше двоичных разрядов, чем помещается в 16-битных регистрах или ячейках памяти, и
возникает "перенос" старшего бита этого числа в бит CF регистра флагов.
Индицирующие флаги процессора дают возможность проанализировать, если это нужно,
результат последней операции и осуществить "разветвление" программы: например, в случае
нулевого результата перейти на выполнение одного фрагмента программы, а в случае
ненулевого - на выполнение другого. Такие разветвления осуществляются с помощью команд
условных переходов, которые в процессе своего выполнения анализируют состояние регистра
флагов. Так, команда - jz zero, осуществляет переход на метку zero, если результат выполнения
предыдущей команды окажется равен нулю (т. е. флаг ZF установлен), а команда - jnc okey,
выполнит переход на метку okey, если предыдущая команда сбросила флаг переноса CF (или
оставила его в сброшенном состоянии).
Управляющий флаг трассировки TF (Trace Flag) используется в отладчиках для
осуществления пошагового выполнения программы. Если TF=l, то после выполнения каждой
команды процессор реализует процедуру прерывания 1 (через вектор прерывания с номером 1).
Управляющий флаг разрешения прерываний IF (Interrupt Flag) разрешает (если равен
единице) или запрещает (если равен нулю) процессору реагировать на прерывания от внешних
устройств.
Управляющий флаг направления DF (Direction Flag) используется особой группой
команд, предназначенных для обработки строк. Если DF=0, строка обрабатывается в прямом
направлении, от меньших адресов к большим; если DF=1, обработка строки идет в обратном
направлении.
Таким образом, в отличие от битов состояния, управляющие флаги устанавливает или
сбрасывает программист, если он хочет изменить настройку системы (например, запретить на
какое-то время аппаратные прерывания или изменить направление обработки строк).

1.4 Защищенный (32-разрядный) режим работы микропроцессора


В программах на языке ассемблера регистры используются очень интенсивно.
Большинство из них имеет определенное функциональное назначение.
На схеме, на рисунке 1.4, представлены регистры общего назначения, сегментные
регистры, состояния и управления 32 разрядных микропроцессоров Intel.

Рисунок 1.4 - Регистры

На рисунке представлены регистры:


- Регистры общего назначения - eax/ax/ah/al, ebx/bx/bh/bl, edx/dx/dh/dl, еcх/cx/ch/cl,
ebp/bp, esi/si, edi/di, esp/sp. Регистры этой группы используются для хранения данных и адресов;
Arhitectura calculatoarelor (lucrări de laborator ) 6
 сегментные регистры cs, ds, ss, es, fs, gs. Регистры этой группы используются для
хранения адресов сегментов в памяти;
 регистр флагов eflags/flags;
 регистр указатель команды eip/ip.
Регистры приведены с наклонной разделительной чертой, это части одного большого 32-
разрядного регистра. Их можно использовать в программе как отдельные объекты. Зачем так
сделано? Для обеспечения работоспособности программ, написанных для младших 16-
разрядных моделей микропроцессоров фирмы Intel, начиная с i8086. 32-разрядные регистры
имеют приставку «е» (Extended).
Сегментные регистры используются в режиме совместимости – в реальном режиме, для
хранения адресов сегментов, а в 32-битном режиме используются для хранения селектора,
который локализует дескриптор сегмента в виртуальной памяти.

1.5 Настройка Visual Studio для 32-битных приложений

Установите Visual Studio Express 2015 for Windows Desktop. Можете проверить
установленную версию Microsoft Assembler проверив файл ml.exe, в директории установки Visual
Studio, путь C: \ Program Files (x86)\ Microsoft Visual Studio 14.0 \ vc \ bin.

Установите файл Irvine_7th_Edition_VS2015.msi в котором находятся множество примеров и


библиотек необходимых для выполнения лабораторных работ. По умолчанию установится в
директории C:\Irvine. Если необходимо создать новый проект- link –
http://kipirvine.com/asm/gettingStartedVS2015/index.htm#CreatingProject).

Часто, программы необходимо запускать без отладчика (Debug). Для этого необходимо
добавить новую команду в меню Debug: Start Without Debugging. Необходимо выполнить
следующие действия:
– В строке меню Tools, выберите Customize.
– Выберите вкладку Commands.
– Установите флажок Menu bar.
– Нажмите кнопку Add Command.
– Выберите Debug из Categories list.
– Выберите Start Without Debugging в правом окне.
– Нажмите кнопку OK.
– Нажмите кнопку Close.

Установка Tab Size в 5

Выберите Options в строке меню Tools. Выберите Text Editor, Выберите All Languages, и
выберите Tabs: установите Tab Size и Indent Size в 5 (рисунок 1.5). Нажмите OK.
Arhitectura calculatoarelor (lucrări de laborator ) 7

Рисунок 1.5

После установки в C:\Irvine, в Visual Studio выберите File> Open> Project/Solution и выберите
путь к файлу C:\Irvine (место установки Irvine_7th_Edition_VS2015.msi):
C:\Irvine > Examples > Project32 > Project.sln
В окне Solution Explorer появится Project.
Выполните нажатие правой кнопки мыши по Project и выберите Add> Existing Item и выберите
путь, к примеру, программы Colors, из Irvine , ch5.
C:\Irvine > Examples > ch5 > 32 bit > colors.asm и выполните нажатие по Add (рисунок 1.6).

Рисунок 1.6

В строке меню Build, выберите Build Project. В окне, внизу окна Visual Studio, появится окно
Output c сообщениями отладчика (рисунок 1.7).
Arhitectura calculatoarelor (lucrări de laborator ) 8

Рисунок 1.7

В окне меню Debug, выберите Start Without Debugging. Появится окно с результатом
выполнения программы– вывод текста – состояние регистров в цвете (рисунок 1.8).

Рисунок 1.8

Для пошаговой отладки программы, выполните нажатие F10 (рисунок 1.9), желтая стрелка
указывает на следующую выполняемую команду. Можно вывести на экран состояние регистров
микропроцессора, выбирая в окне меню Debug, выберите Windows и Registers (только в режиме
отладки). Для вывода состояния переменных, выберите в окне меню Debug, выберите Windows,
Watch>Watch1.
Arhitectura calculatoarelor (lucrări de laborator ) 9

Рисунок 1.9

1.4 Разработка 16-битных программ

Для правильного исполнения 16-ти битных программ необходимо изменить файл


make16_vs2013.bat, который находится в файле Irvine. Для изменения используйте Notepad,
укажите правильный путь к ml.exe, в соответствии с рисунком 1.10.

Рисунок 1.10

Для разработки 16-битных программ необходимо добавить две команды в меню Visual Studio
Tools. Чтобы добавить команду, выберите External Tools в меню Tools. В диалоговом окне,
нажмите на кнопку Add и введите новую команду Build 16-bit ASM (рисунок 1.11). Во вкладке
Command - C:\Irvine\make16_vs2013.bat, т.е. путь к файлу make16_vs2013.bat, который
необходим для разработки 16-битных программ. Arguments и Initial directory можете установить
нажатием кнопок ►, и Aply.
Arhitectura calculatoarelor (lucrări de laborator ) 10

Рисунок 1.10

16-битные программы не выполняются в 64-битных ОС Windows , так как прямо


адресуют hardware и системную память. В данном случае необходимо применить приложение
DOSBox. Установите приложение DOSBox.
Нажмите кнопку Add и введете новую команду Run 16-bit ASM (рисунок 1.12). Во вкладке
Command укажите путь к DOSBox.exe. Установите Prompt for arguments.

Figura 1.12

При нажатии на Run 16-bit ASM, в диалоговое окно Arguments: введите имя файла с
расширением .exe.

Чтобы ряды кода были пронумерованы выберите Tools> Options> Text Editor> All Languages > и
установите, в правом окне, Line Numbers.
Arhitectura calculatoarelor (lucrări de laborator ) 11

Отладка 16, 32-битных программ

Пример 16-битной программы:


INCLUDE Irvine16.inc

.DATA
Vopros byte "Jelaesi stati programistom?-[y/n]",0
Da DB 'Stanesi !',10,13,0
Net DB 'Stanesi filosofom!',0ah,0dh,0
Err BYTE "Jmi pravilino clavisu !",7,0
.CODE ; директива указывает начало сегмента кода
main PROC ;обьявляется процедура с именем main
mov ax,@data ;загрузка регистров сегментов данных ds и
mov ds,ax ;es адресом первого байта данных -@data
mov es,ax

mov ah,00h ;установка графического видеорежима


mov al,12h 640x480, ;text 80x30
int 10h ; BIOS services
mov ax,0600h ; scroll window
mov bh,00000001 ; attribute
mov cx,0506h ; upper-left corner
mov dx,0A40h ; lower-right corner
int 10h ; BIOS services
; write string Vopros
mov al, 1 ; обьявляются параметры для вывода строки со
mov bh, 0 ;смещением (offset) Vopros
mov bl, 10011110b
mov cx, sizeof Vopros
mov dl, 10
mov dh, 7
mov bp, offset Vopros ; 13h – код функции вывода строки
mov ah, 13h ; BIOS services
int 10h
er: mov ah,0 ; get keystroke from keyboard
Int 16h ; BIOS services
cmp al,'y' ; сравнение содержимого регистра al
jz IsDad ; с ASCII кодом ‚y’, сравнение - вычитанием
cmp al,'n' ;условие перехода ZF=1
jz IsNud
mov bp, offset err ; обьявляются параметры для вывода строки с
mov cx, sizeof err ;offset err
mov dh, 10
mov al, 1
mov bl, 010011100b
mov ah, 13h
; 13h – код функции вывода строки
int 10h
; BIOS services
jmp er ; безусловный переход на метку er
IsDad: mov bp, offset da ; обьявляются параметры для вывода строки с
mov cx, sizeof da ;offset da
mov dh, 12
mov al, 1
mov bl, 10011110b
jmp Disp ; безусловный переход на метку Disp
IsNud: mov bp, offset Net ; обьявляются параметры для вывода строки с
Arhitectura calculatoarelor (lucrări de laborator ) 12
mov cx, sizeof net ;offset net
mov dh, 14
mov al, 1
mov bl, 010011100b
Disp: ; метка
mov ah, 13h ; 13h – код функции вывода строки
int 10h ; BIOS services
mov ah,0 ; get keystroke from keyboard
Int 16h ; BIOS services

Exit ; вызов процедуры выхода из программы из


;файла Irvine16.inc
main ENDP ; завершение процедуры main
END main ; завершение программы/ точка входа в программу

Замечание:
1. Файл Irvine16.inc содержит множество процедур, макросов что дополняют исходный код для
отладки программы в Visual Studio;
2. Директива .DATA указывает на начало сегмента данных. В нашем случае этот сегмент
содержит 3 последовательности ASCII символов, коду каждого символа присваивается один байт в
памяти (DB, BYTE), со смещениями (offset) Vopros, Da, Net и Err, значения -13,10
рассматриваются как команды – завершение ряда (LF), с новой строки (CR) соответсвенно;
3. main PROC указывает начало процедуры с именем (смещением) main , которая содержит
основной код программы, main ENDP указывает завершение процедуры. Директива END main
указывает завершение программы (называемой еще и точкой входа в программу), весь код
помещенный после этой директивы, будет игнорироваться ассемблером;
4. В следующих 3 строках происходит инициализация сегментов данных ds и es. Запись @data
определяет сегмент созданный директивой .DATA (или адрес сегмента, адрес первого байта в этом
сегменте);
5. Последовательности символов по смещению Vopros, Da, Net и Err выводятся на экран при
помощи функции BIOS с кодом 13h.
6. Прерывание Int 10h вызывает сервис BIOS (BIOS Services), для выполнения функции, ее код
должен быть загружен в регистре ah.

В процедуре вызова функции (INT) можно указать несколько подфункций. Функция


указывается путем помещения ее номера в регистр AH (подфункция - в регистр AL). Вызов
определенной функции BIOS осуществляется следующим образом:
MOV AH, номер_функции ; указывается номер функции
INT номер_прерывания ; указывается номер прерывания

В программе применены несколько прерываний и функций BIOS:


1) AH=00h - SET VIDEO MODE;
AL=12h - установка графического видеорежима 640x480, text 80x30;
INT 10h - BIOS Services

2) SCROLL UP WINDOW
AH = 06h
AL = number of lines by which to scroll up (00h = clear entire window)
BH = attribute used to write blank lines at bottom of window
CH,CL = row,column of window's upper left corner
DH,DL = row,column of window's lower right corner
INT 10h - BIOS Services

3) WRITE STRING
Arhitectura calculatoarelor (lucrări de laborator ) 13
AH = 13h

AL = write mode
bit 0 - Update cursor after writing
bit 1 - String contains alternating characters and attributes
bits 2-7 - Reserved (0).
BH = page number.
BL = attribute if string contains only characters.
CX = number of characters in string.
DH,DL = row,column at which to start writing.
ES:BP - string to write
INT 10h - BIOS Services

Цвета поля attribute. (character attribute is 8 bit value, low 4 bits set fore color, high 4 bits
set background color)
Код Цвет Код Цвет
0h Чёрный 8h Серый
1h Синий 9h Голубой
2h Зелёный 0Ah Салатовый
3h Бирюзовый 0Bh Светло-бирюзовый
4h Красный 0Ch Розовый
5h Фиолетовый 0Dh Светло-фиолетовый
6h Коричневый 0Eh Жёлтый
7h Белый 0Fh Ярко- белый

4) KEYBOARD - GET KEYSTROKE


AH = 00h
INT 16h - BIOS get keystroke

Return:
AL = ASCII character

Шаблон (template) 16-ти битной программы следующий:

; This program
; Last update:
INCLUDE Irvine16.inc
.data

; определение данных
.code
main PROC
mov ax,@data
mov ds,ax

;код программы

exit
main ENDP
END main
Arhitectura calculatoarelor (lucrări de laborator ) 14
Пример 32-битной программы:

INCLUDE Irvine32.inc
.data
Vopros byte "Jelaesi stati programistom?-[y/n]",0
Da DB 10,13,'Stanesi !',10,13,0
Net DB 10,13,'Stanesi filosofom!',0ah,0dh,0
Err BYTE 10,13,"Jmi pravilino clavisu !",7,0
.code
main PROC

mov edx,OFFSET Vopros


call WriteString

er:
call ReadChar

cmp al,'y'
jz IsDad
cmp al,'n'
jz IsNud
mov edx,OFFSET Err
call WriteString
jmp er

IsDad: mov edx,OFFSET Da


call WriteString
jmp ex
IsNud: mov edx,OFFSET Net
call WriteString

ex:
exit
main ENDP
END main

Замечания:
1. Можно заметить, что эта таже программа, но разработана как 32 битное приложение, в
которой используются процедуры из Irvine32.inc;
2. Определение последовательностей заканчиваются нулем, смещение последовательностей
загружаются в регистр edx (32 бит).
3. Командой call вызываются две процедуры WriteString – вывод последовательности на экран
и ReadChar- ввод символа с клавиатуры. Для вывода некоторой последовательности на
экран, смещение последовательности (offset) необходимо загрузить в регистр edx и вызвать
процедуру. После вызова процедуры ReadChar, в регистр al загрузится код клавиши.

Пример 64-битной программы:

Выберите в Visual Studio проект C:\Irvine\Examples\Project64\Project64.sln.


Выберите пример программы C:\Irvine\Examples\ch3\64 bit\AddTwoSum_64.asm.

; AddTwoSum_64.asm - Ch3 example.

ExitProcess proto

.data
Arhitectura calculatoarelor (lucrări de laborator ) 15
sum qword 0

.code
main proc
mov rax,5
add rax,6
mov sum,rax

mov ecx,0
call ExitProcess

main endp
end

Замечания:
1. Программа суммирут два целых числа и результат сохраняет в переменную (по смещению)
sum (qword – 64 бита).
2. Процедуру выхода из программы ExitProcess, и другие используемые процедуры,
необходимо указывать (декларировать) в начале программы, ExitProcess proto.

1.6 Задания к лабораторной работе


1. Произвести ассемблирование и запуск примеров программ. Исполняемые программы
(.EXE) находятся в Project, с именем Project.exe;
2. Получить и проанализировать листинги программ - Project.lst;
3. Запустите примеры программ в пошаговом режиме в отладчике Debug (последовательное
нажатие F10) и установите окна Registers и Watch1(для переменных).

1.7 Cодержание отчёта


Отчёт по лабораторной работе должен содержать:
- тему и цель работы,
- прокомментированые исходные коды .asm (примеры 16, 32, 64 битных программ);
- файлы листинги (примеры 16, 32, 64 битных программ);
- прокоментированные шаги
выполненые в Debug (screen);
- Выводы.

1.8 Задачи для неаудиторной работы


Вопросы для подготовки студентов к лабораторной работе 2: определите основные типы
даных (байт, слово, двойное слово, учетверенное слово), логическую интерпретацию этих
типов, Указатель на память двух типов, Цепочка, Битовое поле, Неупакованный
(Упакованный) двоично-десятичный тип, Типы данных с плавающей точкой, Типы данных
MMX-расширения, Типы данных XMM-расширения, Представление символов, Системы
счисления, Директивы определения данных, Определение вещественных чисел, команды
двоичной арифметики (ADD, ADC, SUB, SBB, CBW, CWD, CDQ, MUL, IMUL, DIV, IDIV)
Arhitectura calculatoarelor (lucrări de laborator ) 16
ЛАБОРАТОРНАЯ РАБОТА №2
ОСНОВЫ ЯЗЫКА АССЕМБЛЕР
2.1. Цель работы:
Основные типы данных микропроцессора, системы счисления, перевод чисел в различные
системы счисления, директивы резервирования данных, директивы сегментации.
2.2. Общие понятия.
2.2.1. Типы данных.
С точки зрения размерности (физическая интерпретация), микропроцессор аппаратно
поддерживает следующие основные типы данных (рис. 2.1):
 байт — восемь последовательно расположенных битов, пронумерованных от 0 до 7, при
этом бит 0 является самым младшим значащим битом;
 слово — последовательность из двух байт, имеющих последовательные адреса. Размер
слова — 16 бит; биты в слове нумеруются от 0 до 15. Байт, содержащий нулевой бит,
называется младшим байтом, а байт, содержащий 15-й бит - старшим байтом.
Микропроцессоры Intel имеют важную особенность — младший байт всегда хранится по
меньшему адресу. Адресом слова считается адрес его младшего байта. Адрес старшего
байта может быть использован для доступа к старшей половине слова.
 двойное слово — последовательность из четырех байт (32 бита), расположенных по
последовательным адресам. Нумерация этих бит производится от 0 до 31. Слово,
содержащее нулевой бит, называется младшим словом, а слово, содержащее 31-й бит, -
старшим словом. Младшее слово хранится по меньшему адресу. Адресом двойного слова
считается адрес его младшего слова. Адрес старшего слова может быть использован для
доступа к старшей половине двойного слова.
 учетверенное слово — последовательность из восьми байт (64 бита), расположенных по
последовательным адресам. Нумерация бит производится от 0 до 63. Двойное слово,
содержащее нулевой бит, называется младшим двойным словом, а двойное слово,
содержащее 63-й бит, — старшим двойным словом. Младшее двойное слово хранится по
меньшему адресу. Адресом учетверенного слова считается адрес его младшего двойного
слова. Адрес старшего двойного слова может быть использован для доступа к старшей
половине учетверенного слова.

Рисунок 2.1 - Основные типы данных микропроцессора

Кроме трактовки типов данных с точки зрения их разрядности, микропроцессор на уровне


команд поддерживает логическую интерпретацию этих типов (рис. 2.2):
 Целый тип со знаком — двоичное значение со знаком, размером 8, 16, 32 или 64 бита.
Знак в этом двоичном числе содержится в 7, 15, 31 или 63-м бите соответственно. Ноль в
этих битах в операндах соответствует положительному числу, а единица —
отрицательному. Отрицательные числа представляются в дополнительном коде. Числовые
диапазоны для этого типа данных следующие:
– 8-разрядное целое — от –128 до +127;
– 16-разрядное целое — от –32 768 до +32 767;
31 31
– 32-разрядное целое — от –2 до +2 –1;
63 63
– 64-разрядное целое - от –2 до +2 –1
 Целый тип без знака — двоичное значение без знака, размером 8, 16, 32 или 64 бита.
Числовой диапазон для этого типа следующий:
– байт — от 0 до 255;
Arhitectura calculatoarelor (lucrări de laborator ) 17
слово — от 0 до 65 535;

32
– двойное слово — от 0 до 2 –1;
64
– учетверенное слово — от 0 до 2 –1.
 Указатель на память двух типов:
– ближнего типа — 32-разрядный логический адрес, представляющий собой относительное
смещение в байтах от начала сегмента. Эти указатели могут также использоваться в
сплошной (плоской) модели памяти, где сегментные составляющие одинаковы;
– дальнего типа — 48-разрядный логический адрес, состоящий из двух частей: 16-разрядной
сегментной части — селектора, и 32-разрядного смещения.
 Цепочка — представляющая собой некоторый непрерывный набор байтов, слов или
двойных слов максимальной длины до 4 Гбайт.
 Битовое поле представляет собой непрерывную последовательность бит, в которой
каждый бит является независимым и может рассматриваться как отдельная переменная.
Битовое поле может начинаться с любого бита любого байта и содержать до 32 бит.
 Неупакованный двоично-десятичный тип — байтовое представление десятичной цифры
от 0 до 9. Неупакованные десятичные числа хранятся как байтовые значения без знака по
одной цифре в каждом байте. Значение цифры определяется младшим полубайтом.
 Упакованный двоично-десятичный тип представляет собой упакованное представление
двух десятичных цифр от 0 до 9 в одном байте. Каждая цифра хранится в своем полубайте.
Цифра в старшем полубайте (биты 4–7) является старшей.

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

Отметим, что “Зн” на рис. 2.2 означает знаковый бит.


 Типы данных с плавающей точкой. Сопроцессор имеет несколько собственных типов
данных, несовместимых с типами данных целочисленного устройства.
 Типы данных MMX-расширения (Pentium MMX/II). Данный тип данных появился в
микропроцессоре Pentium MMX. Он представляет собой совокупность упакованных
целочисленных элементов определенного размера.
 Типы данных XMM-расширения (Pentium III/IV). Этот тип данных появился в микропроцессоре
Pentium III. Он представляет собой совокупность упакованных элементов с плавающей точкой
фиксированного размера.

Данные с плавающей точкой.


Основной тип данных, с которыми работает сопроцессор — вещественный. Данные этого типа
описываются тремя форматами: коротким, длинным и расширенным (рис. 2.3).
Arhitectura calculatoarelor (lucrări de laborator ) 18

Рисунок 2.3 - Форматы вещественных чисел сопроцессора

Для представления вещественного числа используется формула (2.1):


A = (±M)*N  (р), (2.1)
где М — мантисса числа А. Мантисса должна удовлетворять условию |М|<1;
N — основание системы счисления, представленное целым положительным числом;
р — порядок числа, показывающий истинное положение точки в разрядах мантиссы (по этой
причине вещественные числа имеют еще название чисел с плавающей точкой, так как ее
положение в разрядах мантиссы зависит от значения порядка).
Для удобства обработки в компьютере чисел с плавающей точкой, архитектурой компьютера на
компоненты формулы (2.1) накладываются некоторые ограничения. Для сопроцессоров,
применяющихся в архитектуре Intel, эти условия и ограничения заключаются в следующем:
 Основание системы счисления N=2.
 Мантисса М должна быть представлена в нормализованном виде. Нормализация может
отличаться для разных типов процессоров. Например, мантисса нормализованного числа
должна удовлетворять условию: 1/N≤|M|<1. Это означает, что старший бит представления
должен быть единичным. Для случая, когда N=2 это соответствует отношению 1/2≤|М|<1
или в двоичном виде 0,10...00≤|М|<0,11...11, то есть первая цифра после запятой должна
быть значащей (единицей), а порядок р, соответственно, таким, чтобы это условие
выполнялось. Для архитектуры микропроцессора Intel нормализованным является число
несколько иного вида:
А = (-1)S*Nq*M (2.2)
где S — значение знакового разряда:
0 — число больше нуля;
1 — число меньше нуля;
р — порядок числа. Его значение аналогично значению порядка р в формуле (2.1).

В этой формуле знак имеют и порядок вещественного числа, и его мантисса. На рис. 2.3
видно, что формат хранения вещественного числа в памяти имеет только поле для знака мантиссы.
А где же хранится знак порядка?
В сопроцессоре Intel на аппаратном уровне принято соглашение, что порядок р определяется в
формате вещественного числа особым значением, называемым характеристикой q. Величина q
связана с порядком р посредством формулы (2.3) и представляет собой некоторую константу.
Условно назовем ее фиксированным смещением.
q=p+фикcиpoвaннoe смещение (2.3)
Для каждого из трех возможных форматов вещественных чисел смещение q имеет разное, но
фиксированное для конкретного формата значение, которое зависит от количества разрядов,
отводимых под характеристику (табл. 2.1).
В табл. 2.1 показаны диапазоны значений характеристик q и соответствующих им истинных
порядков р вещественных чисел. Отметим, что нулевому порядку вещественного числа в коротком
формате соответствует значение характеристики равное 127, которому в двоичном представлении
соответствует значение 0111 1111. Отрицательному порядку р, например, -1, будет соответствовать
характеристика q=-1+127=126. В двоичном виде ей соответствует значение 0111 1110.

Таблица 2.1 - Формат вещественных чисел


Формат Короткий Длинный Расширенный
Длина числа (биты) 32 64 80
Размерность мантиссы, М 24 53 64
Arhitectura calculatoarelor (lucrări de laborator ) 19
-38 +38 -308 +308 -4932 +4932
Диапазон значений 10 …10 10 …10 10 …10
Размерность 8 11 15
xарактеристики, q
Значение фиксированного +127 +1023 +16383
смещения
Диапазон характеристик, q 0…255 0…2047 0…32767
Диапазон порядков, р -126...+127 -1022...+1023 -16382...+ 16383

Положительному порядку р, например, +1, будет соответствовать характеристика q=l+127=128,


в двоичном виде ей соответствует значение 1000 0000. То есть, все положительные порядки имеют в
двоичном представлении характеристики старший бит равный единице, а отрицательные порядки —
нет. А знак порядка находится в старшем бите характеристики. Теперь вам должно быть понятно,
откуда появились значения в двух последних строках табл. 2.1.
Так как нормализованное вещественное число всегда имеет целую единичную часть (исключая
представление перечисленных выше специальных численных значений), то при его представлении
в памяти появляется возможность считать первый разряд вещественного числа единичным по
умолчанию и учитывать его наличие только на аппаратном уровне. Это дает возможность увеличить
диапазон представимых чисел, так как появляется лишний разряд, который можно использовать
для представления мантиссы числа. Но это справедливо только для короткого и длинного форматов
вещественных чисел. Расширенный формат, как внутренний формат представления числа
любого типа в сопроцессоре, содержит целую единичную часть вещественного в явном виде.
Как определить вещественное число или зарезервировать место для его размещения в
программе на ассемблере?
Короткое вещественное число длиной в 32 разряда определяется директивой REAL4. При
этом обязательным в записи числа является наличие десятичной точки, даже если оно не имеет
дробной части. Для транслятора десятичная точка является указанием, что число нужно
представить в виде числа с плавающей точкой в коротком формате (рис. 2.3). Это же касается
длинного и расширенного форматов представления вещественных чисел, определяемых
директивами REAL8 и REAL10. Другой способ задания вещественного числа директивами
REAL4, REAL8 и REAL10— экспоненциальная форма с использованием символа «е». Вид
вещественного числа в поле операндов директив REAL4, REAL8 и REAL10 можно представить
синтаксической диаграммой (рис. 2.4).

Рисунок 2.4 - Синтаксис вещественных чисел


в директивах REAL4, REAL8 и REAL10

Чтобы окончательно разобраться с тонкостями представления вещественных чисел


различных форматов в памяти, рассмотрим несколько примеров.
Пример 2.1. Определение вещественного числа короткого формата. Определим в
программе вещественное число 45,56 в коротком формате:
REAL4 45.56 или
REAL4 45.56e0 или
REAL4 0.4556е2
В памяти это число будет выглядеть так: 71 3d 36 42.
Учитывая, что в архитектуре Intel принят перевернутый порядок следования байт в памяти в
соответствии с принципом «младший байт по младшему адресу», истинное представление
числа 45,56 будет следующим:
42 36 3d 71
В двоичном представлении в памяти это будет иметь вид, как на рис. 2.5.
Arhitectura calculatoarelor (lucrări de laborator ) 20
На рис. 2.5 видно, что старшая единица мантиссы, при представлении в памяти,
отсутствует.

Рис. 2.5. Двоичное представление в памяти вещественного числа в директиве dd.

Пример 2.2. Определение вещественного числа длинного формата. Определим в программе


вещественное число 45,56 в длинном формате:
REAL8 45.56 или
REAL8 45.56e0
В памяти это число будет выглядеть так: 47 el 7a 14 ае с7 46 40
Перевернув его, получим истинное значение: 40 46 с7 ае 14 7а el 47
Разберите его по компонентам вещественного числа самостоятельно.
Пример 2.3. Определение вещественного числа расширенного формата.
Определим в программе вещественное число 45,56 в расширенном формате представления:
REAL10 45.56
В памяти это число будет выглядеть так: 71 3d 0а d7 а3 70 3d b6 04 40.
Перевернув его, получим истинное значение в памяти: 40 04 b6 3d 70 а3 d7 0а 3d 71.
А вот его двоичное представление полезно рассмотреть подробнее (рис. 2.6):

Рис. 2.6. Двоичное представление в памяти вещественного числа в директиве dt.

0100 0000 0000 0100 1011 0110 0011 1101 0111 0000 1010 0011 1101 0111 0000
1010 1110 0001 0111 0001
Данное число имеет следующее назначение битов:
 0 — знак «+»;
 100 0000 0000 0100 - характеристика q=1,6388;
 1011 01 — целая часть числа (45);
 10 0011 1101 0111 0000 1010 0011 1101 0111 0000
1010 1110 0001 0111 0001 - дробная часть числа (0,56).
Как видно, в мантиссе явно присутствует старшая единица, чего не было в коротком и длинном
форматах представления вещественного числа.
Дальнейшее обсуждение требует четкого понимания того, каким образом из дробного десятичного
числа получается его расширенное вещественное представление. Рассмотрим этот процесс по
шагам.
 Переведем десятичную дробь 45,56 в двоичное представление. Подробно алгоритм такого
перевода описан ниже. В результате получим эквивалентное двоичное представление:
45,5610 =101101.1000111101011100001010001
111010111000010101110000101110001
 Нормализуем число. Для этого переносим точку влево, до тех пор, пока в целой части числа
не останется одна двоичная единица. Число переносов влево (или вправо, если десятичное
число было меньше единицы) будет являться порядком числа. Но будьте внимательны — в
Arhitectura calculatoarelor (lucrări de laborator ) 21
поле характеристики заносится смещенное значение порядка (табл. 2.1). Таким образом, после
перемещения точки получаем значение порядка равное 5. Соответственно, характеристика
будет выглядеть так: 5+16383=1638810=1000000000001002.
Сформированный результат в виде вещественного числа в расширенном формате состоит из трех
компонент:
 знака — 0;
 характеристики – 100000000000100;
 мантиссы – 1011 0110 0011 1101 0111 0000 1010 0011
1101 0111 0000 1010 1110 0001 0111 0001
Само преобразование производится автоматически при загрузке числа в стек сопроцессора.
Исключение составляет расширенный формат.

Представление символов.
К символьной информации относятся буквы алфавитов, цифры и другие специальные
знаки. Все данные, поступающие в машину с клавиатуры, или выводимые с процессора на
принтер или экран дисплея, представляются в кодах ASCII (American Standard Code for
Information Interchange — Американский стандартный код для обмена информацией).
Следовательно, одной из основных функций процессора является преобразование внешнего
формата данных во внутренний двоичный формат. Стандартный ASCII код состоит из 7 бит
(128 различных символов от 00h до 7fh), т.е. символ ASCII представлен одним байтом, старший
бит которого равен нулю. Например, последовательность 4D: 49: 43: 52: 4F в коде ASCII задает
слово MICRO.

2.2.2. Системы счисления


Системы счисления подразделяются на позиционные и непозиционные. Как в
позиционных, так и в непозиционных системах счисления используется определенный набор
символов — цифр, последовательное сочетание которых образует число.
В позиционной системе счисления количество символов в наборе равно основанию
системы счисления. Место каждой цифры в числе называется позицией. Номер позиции символа
(за вычетом единицы) в числе называется разрядом. Разряд 0 называется младшим разрядом.
Каждой цифре соответствует определенный количественный эквивалент. Введем обозначение
— запись А(P) будет означать количественный эквивалент числа А, состоящего из п цифр ak (где
k =0, …, п -1) в системе счисления с основанием р. Это число можно представить в виде
последовательности цифр:
A(P) = an-1 * an-2 … a1 * a0
При этом, конечно, всегда выполняется неравенство ak <р.
В общем случае количественный эквивалент некоторого положительного числа А в
позиционной системе счисления можно представить выражением:
A(P) = an-1 * pn-1 + an-2 * pn-2 + …+ a1 * p1 + a0 * p0 (2.4)

где р — основание системы счисления (некоторое целое положительное число);


а — цифра данной системы счисления; п — номер старшего разряда числа.
Двоичная система счисления
Набор цифр для двоичной системы счисления — {0,1}, основание степени р= 2.
Количественный эквивалент некоторого целого n-значного двоичного числа вычисляется
согласно формуле (2.4):
A(2) = an-1*2n-1 + an-2*2n-2 + …+ a1*21 + a0*20 (2.5)
Как мы уже отмечали, наличие этой системы счисления обусловлено тем, что компьютер
построен на логических схемах, имеющих в своем элементарном виде только два состояния —
включено и выключено. Например, рассмотрим двоичное число 10100111. Вычислим
десятичный эквивалент этого двоичного числа. Согласно формуле (2.5), это будет величина,
равная следующей сумме:
1*27 + 0*26 +1*25 +0*24 +0*23 +1*22 +1*21 +1*20
Посчитайте сами, сколько получится.
Шестнадцатеричная система счисления
Arhitectura calculatoarelor (lucrări de laborator ) 22
Шестнадцатеричная система счисления имеет набор цифр {0, 1, 2, ..., 9, А, В, С, D, Е, F} и
основание степени (р)=16. Количественный эквивалент некоторого целого n-значного
шестнадцатеричного числа вычисляется согласно формуле (2.4):
A(16) = an-1*16n-1 + an-2*16n-2 + …+ a1*161 + a0*160
К примеру, количественный эквивалент шестнадцатеричного числа f45ed23c равен:
15*167+4*166+5*165+14*164+13*163+2*162+3*161+12*160.
Перевод в двоичную систему счисления
Перевод из десятичной системы счисления
Перевод числа в двоичную систему счисления из десятичной выполняется по описанному
далее алгоритму.
 Разделить десятичное число А на 2. Запомнить частное q и остаток а.
 Если в результате шага 1 частное q не равно 0, то принять его за новое делимое и отметить
остаток а, который будет очередной значащей цифрой числа, и вернуться к шагу 1, на
котором в качестве делимого (десятичного числа) участвует полученное на шаге 2 частное.
 Если в результате шага 1 частное q равно 0, алгоритм прекращается. Выписать остатки в
порядке, обратном их получению. Получится двоичный эквивалент исходного числа.
К примеру, перевод в двоичную систему счисления числа 24710 иллюстрирует рис.2.7.

Рис.2.7. Перевод в двоичную систему счисления


Порядок обхода остатков для получения результата (111101112) показан стрелками.
Перевод из шестнадцатеричной системы счисления
Перевод из шестнадцатеричной системы счисления заключается в последовательной замене
шестнадцатеричных цифр соответствующими двоичными тетрадами. К примеру, двоичное
число, соответствующее числу e4d516, равно 1110 0100 1101 01012 .

Перевод в шестнадцатеричную систему счисления


Перевод из десятичной системы счисления
Общая идея алгоритма перевода из десятичной системы счисления в шестнадцатеричную
аналогична рассмотренной ранее в алгоритме перевода в двоичную систему счисления из
десятичной.
 Разделить десятичное число А на 16. Запомнить частное q и остаток а.
 Если в результате шага 1 частное q не равно 0,то принять его за новое делимое, записать
остаток и вернуться к шагу 1.
 Если частное q равно 0,прекратить работу алгоритма. Выписать остатки в порядке,
обратном их получению. Получится шестнадцатеричный эквивалент исходного
десятичного числа.
К примеру, перевод в шестнадцатеричную систему счисления числа 32 767 16 иллюстрирует
рис.2.8. Порядок обхода остатков для получения результата (7fff16) показан стрелками.
Arhitectura calculatoarelor (lucrări de laborator ) 23

Рис.2.8. Перевод в шестнадцатеричную систему счисления.

Перевод из двоичной системы счисления


Идея алгоритма состоит в том, что двоичное число разбивается на тетрады начиная с
младшего разряда. Далее каждая тетрада приводится к соответствующему шестнадцатеричному
числу.
К примеру, пусть требуется перевести в шестнадцатеричную систему счисления следующее
число:
111001011010111101011000110110001111010101011012 .
Разобьем его на тетрады:
0111 0010 1101 0111 1010 1100 0110 1100 0111 1010 1010 1101.
По тетрадам приводим последовательности нулей и единиц к шестнадцатеричному
представлению:
7 2 d 7 a c 6 c 7 a a d.
Перевод дробных чисел
Давайте разберемся с наиболее часто используемыми на практике способами перевода
дробных чисел. Для этого формулу (2.4) преобразуем к следующему виду:
A(P) = an-1*pn-1 + an-2*pn-2 + …+ a1* p1 + a0* p0 + a -1* p-1+ a -2* p-2 …+ a-m* p-m . (2.6)
Рассмотрим операции перевода чисел па примерах.
Пример 2.4
Пусть требуется перевести в десятичное представление, следующее дробное число,
заданное в двоичной системе счисления:
110100,010010112 .
Для перевода используем формулу (2.6):
110100,010010112=1*25+1*24+0*23+1*22+0*21+0*20 +0*2-1+1*2-2+0*2-3+
+0*2-4+1*2-5+0*2-6+1*2-7+1*2-8.
Пример 2.5
Пусть требуется перевести в десятичное представление следующую дробь, заданную в
шестнадцатеричной системе счисления: 1df2,ale416 .
Вновь используем формулу (2.6):
Idf2,ale416=l*163 + 13*162+15*21+2*160 + 10*16-1 +l*16-2+14*16-3+4*16-4.

Пример 2.6
Рассмотрим теперь проблему представления десятичных дробей в двоичной и
шестнадцатеричной системах счисления.
Общий алгоритм перевода десятичной дроби в другую систему счисления можно представить
следующей последовательностью шагов.
 Выделить целую часть десятичной дроби и выполнить ее перевод в выбранную систему
счисления по алгоритмам, рассмотренным ранее.
 Выделить дробную часть и умножить ее на основание выбранной новой системы
счисления.
 B полученной после умножения дробной части десятичной дроби выделить целую часть и
принять ее в качестве значения первого после запятой разряда числа в новой системе
счисления.
 Если дробная часть значения, полученного после умножения, равна нулю, то прекратить
процесс перевода. Процесс перевода можно прекратить также в случае, если достигнута
необходимая точность вычисления. В противном случае вернуться к шагу 3.
Arhitectura calculatoarelor (lucrări de laborator ) 24

Рис.2.9. Перевод целой части десятичного числа 108,406


в двоичную систему счисления.
Рассмотрим пример. Пусть требуется перевести в двоичную систему счисления десятичную
дробь 108,40610 .
Сначала переведем целую часть десятичной дроби 108,406 10 в двоичную систему счисления
(рис.2.9).
Затем переведем дробную часть десятичного числа 108,406 10 (рис.2.10) по приведенному ранее
алгоритму.

Рис.2.10. Перевод дробной части числа 108,406


в двоичную систему счисления.

Результат перевода следующий:


108,40610 =1101100,011001111.
При переводе дробного числа из десятичной системы счисления в шестнадцатеричную
целесообразно предварительно перевести число в двоичную систему, а затем двоичное
представление разбить на тетрады отдельно до разделительной запятой и после запятой.
Разбиение на тетрады двоичных разрядов целой части производят от запятой в сторону
старших разрядов. Неполную старшую тетраду дополняют слева нулями. Разряды дробной
части, напротив, разбивают на тетрады от запятой вправо к младшим разрядам. Если последняя
тетрада неполная, то ее дополняют нулями справа. На рис.2.11 показан процесс перевода того
же дробного десятичного числа (108,40610) в эквивалентное шестнадцатеричное представление.

Рис.2.11. Пример перевода десятичного числа в шестнадцатеричную


систему счисления.
Arhitectura calculatoarelor (lucrări de laborator ) 25
Перевод чисел со знаком
Положительные целые со знаком — это 0 и все положительные числа. Отрицательные
целые со знаком — это все числа, меньшие 0. Отличительным признаком числа со знаком
является особая трактовка старшего бита поля, представляющего число. В качестве поля могут
выступать байт, слово или двойное слово. Если старший бит, равен 0, число считается
положительным и его значение вычисляется по правилам, которые мы рассмотрели ранее. Если
старший бит равен 1, число считается отрицательным, а это предполагает, что оно записано в
так называемом дополнительном коде.
Дополнительный код некоторого отрицательного числа представляет собой результат
инвертирования (замены 1 на 0 и наоборот) каждого бита двоичного числа, равного модулю
отрицательного исходного числа плюс единица. К примеру, рассмотрим десятичное число
-18510. Модуль данного числа в двоичном представлении равен 1011 1001 2. Сначала нужно
дополнить это значение слева нулями до нужной размерности — байта, слова и т.д. В нашем
случае дополнить нужно до слова, так как диапазон представления знаковых чисел в байте
составляет -128...127. Следующее действие — получить двоичное дополнение. Для этого все
разряды двоичного числа нужно инвертировать:
00000000101110012 ->11111111010001102 .
Теперь прибавляем единицу:
11111111010001102 +00000000000000012 =11111111010001112 .
Результат преобразования равен 11111111010001112. Именно так и представляется число -185 10 в
компьютере.
При работе с числами со знаком от вас наверняка потребуется умение выполнять обратное
действие — имея двоичное дополнение числа, определить значение его модуля. Для этого
необходимо выполнить два действия.
 Выполнить инвертирование битов двоичного дополнения.
 К полученному двоичному числу прибавить двоичную единицу.
К примеру, определим модуль двоичного представления числа -185=1111111101000111 2
Сначала инвертируем биты:
11111111010001112 ->00000000101110002 .
Добавляем двоичную единицу:
00000000101110002 +00000000000000012 =00000000101110012 =|-185|.

2.3 Директивы определения данных

Директивы определения данных в ассеблере (MASM) следующие:


Директивы BYTE и SBYTE - для определения байта.
Директивы BYTE и SBYTE определяют выражение или константу, принимающую
значение из диапазона:
 для чисел со знаком –128...+127;
 для чисел без знака 0...255;
 символьную строку из одного или более символов. Строка заключается в кавычки.
В этом случае определяется столько байт, сколько символов в строке.

value1 BYTE 'A' ; символ ASCII


value2 BYTE 0 ; byte без знака
value3 BYTE 255 ; byte без знака
value4 SBYTE −128 ; byte со знаком
value5 SBYTE +127 ; byte со знаком
value6 BYTE ? ; байт с неопределенным значением

Также можно использовать директиву db (define byte):

Пример:
alfa DB 65, 72h, 75o, 11011b, 11h+22h, 0ach
DB -65, 'a', 'abc'
Arhitectura calculatoarelor (lucrări de laborator ) 26
В памяти, начиная с символического адреса alfa, будет сформирована
последовательность байтов (значения в шестнадцатеричном коде) :

41 72 3d 1b 33 ac bf 61 61 62 63
alfa +0 +1 +2 +3 +4
Двоичное значение 11011b будет расположена по адресу alfa+3.

Определение символьной строки:


privet1 BYTE "Good afternoon",0
greeting2 BYTE 'Good night',0

Применение оператора DUP (duplicate):


BYTE 20 DUP(0) ; 20 bytes, с нулевым содержанием
BYTE 20 DUP(?) ; 20 bytes, содержание неопределено
BYTE 4 DUP("STACK") ; 20 bytes: "STACKSTACKSTACKSTACK"

WORD şi SWORD — резервирование памяти для данных размером 2 байта (без и со


знаком).
Этими директивами можно задавать следующие значения:
 выражение или константу, принимающую значение из диапазона:
 для чисел со знаком –32 768...32 767;
 для чисел без знака 0...65 535;
 выражение, занимающее 16 или менее бит, в качестве которого может выступать
смещение в 16-битовом сегменте или адрес сегмента;
 или 2-байтовую строку, заключенная в кавычки.

word1 WORD 65535 ; без знака


word2 SWORD -32768 ; со знаком
word3 WORD ? ; с неопределенным значением

Также можно использовать директиву dw (define word):

beta DW 4567h, 0bc4ah, 1110111011b, 2476o


DW -7683, 7683, 'ab'

В памяти с адреса (смещения, offset) “beta” будут расположены байты:

67 45 4a bc bb 03 3e 05 fd e1 03 e1 62 61
beta +2 +4 +6 +8 +12

Восьмеричное значение 2476o будет рассположено в памяти со смещением beta +6.

DWORD и SDWORD — резервирование памяти для данных размером 4 байта (без и


со знаком).
Директивами можно задавать следующие значения:
 выражение или константу, принимающую значение из диапазона:
 для чисел со знаком –231...+231-1;
 для чисел без знака 0...232-1;
 относительное или адресное выражение, состоящее из 16-битового адреса
сегмента и 16-битового смещения;
 вещественное число одинарной точности.

val1 DWORD 12345678h ; без знака


val2 SDWORD −21474836 ; со знаком
val3 DWORD 20 DUP(?) ; без знака
Arhitectura calculatoarelor (lucrări de laborator ) 27

Также можно использовать директиву dd (Define Double word):


val1 DD 12345678h, −21474836

QWORD и DQ (Define Quad – word) — резервирование памяти для данных размером 8


байт.
Директивами можно задавать следующие значения:
 выражение или константу, принимающую значение из диапазона:
 для чисел со знаком -263…+263-1;
 для чисел без знака 264-1;
 вещественное число двойной точности.

quad1 QWORD 1234567812345678h

quad2 DQ 1234567812345678h

TBYTE и DT — резервирование памяти для данных размером 10 байт.


Директивами можно задавать следующие значения:
 упакованную десятичную константу в диапазоне 0...99 999 999 999 999 999 999.
 вещественное число повышенной точности.

intVal TBYTE 80000000000000001234h


intVal1 DT 80000000000000001234h

Определение вещественных чисел.


Вещественные числа определяются следующими директивами:
 REAL4 – определение 32 разрядных вещественных чисел;
 REAL8 – определение 64 разрядных вещественных чисел двойной точности;
 REAL10 – определение 80 разрядных вещественных чисел повышенной точности.

rVal1 REAL4 -1.2


rVal2 REAL8 3.2E-260
rVal3 REAL10 4.6E+4096
ShortArray REAL4 20 DUP(0.0)

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


rVal1 DD -1.2
rVal2 DQ 3.2E-260
rVal3 DT 4.6E+4096

Диапазон значений может быть:

REAL4 - 1.18 x10-38 до 3.40 x1038


REAL8 - 2.23x10-308 до 1.79x10308
REAL10 - 3.37x10-4932 до 1.18x104932

Используется еще следующие типы:


 Неупакованный двоично-десятичный тип — байтовое представление десятичной цифры
от 0 до 9. Неупакованные десятичные числа хранятся как байтовые значения без
знака по одной цифре в каждом байте. Значение цифры определяется младшим
полубайтом.
 Упакованный двоично-десятичный тип представляет собой упакованное представление
двух десятичных цифр от 0 до 9 в одном байте. Каждая цифра хранится в своем
полубайте. Цифра в старшем полубайте (биты 4–7) является старшей.
Arhitectura calculatoarelor (lucrări de laborator ) 28

BCD1 byte 03h, 06h ; неупакованный двоично-десятичный тип


BCD2 byte 33h, 96h ; упакованный двоично-десятичный тип

Важно уяснить себе порядок размещения данных в памяти. Он напрямую связан с логикой
работы микропроцессора с данными. Микропроцессоры Intel требуют следования данных в
памяти по принципу: младший байт по младшему адресу.

Пример 2.7. Рассмотрим следующую программу как пример применения директив


резервирования данных.
INCLUDE Irvine32.inc
.data
alfa byte 40+25,72h,75o,11011b,15+22h,0ach
sbyte -65
byte 'a','abc'
beta word 4567h,0bc4ah,1110111011b,2476o
dw -7683,7683,'ab'
gama dword 12345678h,1
sdword -1,-1.0,-0.23828125
dword 1.0,0.0390625
gama1 dword gama
qw qword 2,-2,2.5,-2.5
t1 tbyte 8888888888777777777h,80555555555555555555h
tb tbyte 45671234567890123456h,80456712345678901234h
f tbyte 3345,-3345,1.0,-1.0

.code
main proc

exit
main ENDP
END main

Полученный файл листинга (.lst) после ассемблирования (с 765 строки)::


C .LIST
C
00000000 .data
00000000 41 72 3D 1B 31 alfa byte 40+25,72h,75o,11011b,15+22h,0ach
AC
00000006 BF sbyte -65
00000007 61 61 62 63 byte 'a','abc'
0000000B 4567 BC4A 03BB beta word 4567h,0bc4ah,1110111011b,2476o
053E
00000013 E1FD 1E03 6162 dw -7683,7683,'ab'
00000019 12345678 gama dword 12345678h,1
00000001
00000021 FFFFFFFF sdword -1,-1.0,-0.23828125
BF800000
BE740000
0000002D 3F800000 dword 1.0,0.0390625
3D200000
00000035 00000019 R gama1 dword gama
00000039 qw qword 2,-2,2.5,-2.5
0000000000000002
FFFFFFFFFFFFFFFE
4004000000000000
Arhitectura calculatoarelor (lucrări de laborator ) 29
C004000000000000
00000059 t1 tbyte 8888888888777777777h,80555555555555555555h
08888888888777777777
80555555555555555555
0000006D tb tbyte 45671234567890123456h,80456712345678901234h
45671234567890123456
80456712345678901234
00000081 f tbyte 3345,-3345,1.0,-1.0
00000000000000000D11
FFFFFFFFFFFFFFFFF2EF
3FFF8000000000000000
BFFF8000000000000000

00000000 .code
00000000 main proc

exit
00000000 6A 00 * push +000000000h
00000002 E8 00000000 E * call ExitProcess
00000007 main ENDP
END main
_Microsoft (R) Macro Assembler Version 12.00.31101.0 02/16/15 19:52:47
..\lab21.asm Symbols 2 - 1

Первый столбец показывает смещение внутри сегмента в 16-ом формате. Потом следует
поле данных соответсвующей строки. Целые отрицательные числа представленны в
дополнительном коде, а вещественные в формате чисел с плавающей точкой. Буква R указывают
на то, что имеется смещение 0000019h, т.е. относительный адрес переменной gama.
В переменных t1, tb, в формате BCD, можно заметить бит знака. В файле .lst не указываютя
физические адреса байтов в памяти.

2.4 Двоичная арифметика

Выполнение этих команд влияет на флаги регистра EFLAGS.

ADD (ADDition) - Сложение


Схема команды: add приемник,источник
Назначение: сложение двух операндов источник и приемник размерностью 8, 16, 32 и 64 бита.
Алгоритм работы:
 сложить операнды источник и приемник;
 записать результат сложения в приемник;
 установить флаги.
Выполнение команды влияет на состояние флагов: CF (флаг переноса), ZF (флаг нуля), SF (флаг
знака), OF (флаг переполнения), AF (флаг служебного переноса), PF (флаг четности).
Применение:
Для операндов команды ADD нужно соблюдать те же правила и ограничения, что и для
операндов команды МОV. Команда add используется для сложения двух целочисленных
операндов. Результат сложения помещается по адресу первого операнда. Если результат
сложения выходит за границы операнда приемник (возникает переполнение), то учесть эту
ситуацию следует путем анализа флага cf и последующего возможного применения команды
adc.
Ex.:
add ax, 5
add bl, 5
add ax, bx
Arhitectura calculatoarelor (lucrări de laborator ) 30
add word ptr [bx], 75
add alfa, ax
add alfa, 5
add byte ptr [si], 75
add byte ptr alfa, 75

.data
var1 DWORD 10000h
var2 DWORD 20000h
.code
mov eax,var1 ; EAX = 10000h
add eax,var2 ; EAX = 30000h

.data
sum qword 0

.code
mov rax,5
add rax,6
mov sum,rax

ADC (Addition with Carry) - Сложение с переносом


Схема команды: adc приемник,источник
Назначение: сложение двух операндов с учетом переноса из младшего разряда.
Алгоритм работы:
 сложить два операнда;
 поместить результат в первый операнд: приемник=приемник+источник+cf;
 в зависимости от результата установить флаги.
Выполнение команды влияет на состояние флагов: OF SF ZF AF PF CF
Применение:
Команда adc используется при сложении длинных двоичных чисел. Ее можно
использовать как самостоятельно, так и совместно с командой add. При совместном
использовании команды adc с командой add сложение младших байтов/слов/двойных слов
осуществляется командой add, а уже старшие байты/слова/двойные слова складываются
командой adc, учитывающей переносы из младших разрядов в старшие. Таким образом,
команда adc значительно расширяет диапазон значений складываемых чисел.
.data
sl1 dd 01fe544fh
sl2 dd 005044cdh
rez dd 0
.code
...
mov ax,sl1
add ax,sl2 ;сложение младших слов слагаемых
mov rez,ax
mov ax,sl+2
adc ax,sl2+2 ;сложение старших слов слагаемых плюс cf
mov rez+2,ax

Также можно сложить два целых 32-битных числа (FFFFFFFFh + FFFFFFFFh), сохраняя
результат в паре регистров EDX:EAX: 00000001FFFFFFFEh:
mov edx,0
mov eax,0FFFFFFFFh
add eax,0FFFFFFFFh
Arhitectura calculatoarelor (lucrări de laborator ) 31
adc edx,0

SUB (SUBtract) - Вычитание


Схема команды: sub операнд_1,операнд_2
Назначение: целочисленное вычитание.
Алгоритм работы:
 выполнить вычитание операнд_1=операнд_2-операнд_1;
 установить флаги.
Выполнение команды влияет на состояние флагов: OF SF ZF AF PF CF
Применение:
Для операндов команды SUB нужно соблюдать те же правила и ограничения, что и для
операндов команды МОV. Команда sub используется для выполнения вычитания
целочисленных операндов или для вычитания младших частей значений многобайтных
операндов.

.data
var1 DWORD 30000h
var2 DWORD 10000h
.code
mov eax,var1 ; EAX = 30000h
sub eax,var2 ; EAX = 20000h

SBB (SuBtract with Borrow) - Вычитание с заемом


Схема команды: sbb операнд_1,операнд_2
Назначение: целочисленное вычитание с учетом результата предыдущего вычитания командами
sbb и sub (по состоянию флага переноса cf).
Алгоритм работы:
 выполнить сложение операнд_2=операнд_2+(cf);
 выполнить вычитание операнд_1=операнд_1-операнд_2;
Выполнение команды влияет на состояние флагов: OF SF ZF AF PF CF
Применение:
Команда sbb используется для выполнения вычитания старших частей значений многобайтных
операндов с учетом возможного предыдущего заема при вычитании младших частей значений
этих операндов.
Ex. Op1 dword 12345678h
Op2 dword 0abcdef78h
Rez dword ?
.............
mov ax, word ptr op1
subax, word ptr op2
mov word ptr rez, ax
mov ax, word ptr op1 + 2
sbbax, word ptr op2 + 2 ; учитывается возможный заем
mov word ptr rez + 2, ax

Ех.. Вычитание из 64 битного числа 32 битного. Пара регистров EDX:EAX загружактся


значением 0000000700000001h и вычитаем 2. На первом этапе – вычитание младших частей
(EAX-00000001h), т. е. из 1 вычитаем 2, что приведет к заему с установлением флага Carry.

mov edx,7 ; старшая часть 
mov eax,1 ; младшая часть
sub eax,2 ; вычитаем  2
sbb edx,0 ; вычитаем старшую часть
Arhitectura calculatoarelor (lucrări de laborator ) 32

CBW (Convert Byte to Word) - Преобразование байта в слово


Схема команды: cbw
Назначение: расширение операнда со знаком.
Алгоритм работы:
cbw — при работе команда использует только регистры al и ax:
 анализ знакового бита регистра al:
 если знаковый бит al=0, то ah=00h;
 если знаковый бит al=1, то ah=0ffh.
Состояние флагов после выполнения команды: выполнение команды не влияет на флаги
Применение:
Данные команды используются для приведения операндов к нужной размерности с
учетом знака. Такая необходимость может, в частности, возникнуть при программировании
арифметических операций.

Ех.:
a sbyte -75
b sword -188
c sword ?
.........
mov al, a
cbw ; преобразование байта в слово
add ax, b
mov c, ax
..........

CWD (Convert Word to Double word) - Преобразование слова в двойное слово


Схема команды: cwd
Назначение: расширение слова со знаком до размера двойного слова со знаком.
Алгоритм работы: копирование значения старшего бита регистра ax во все биты регистра dx.
Состояние флагов после выполнения команды: выполнение команды не влияет на флаги
Применение:
Команда cwd используется для расширения значения знакового бита в регистре ax на
биты регистра dx. Данную операцию, в частности, можно использовать для подготовки к
Arhitectura calculatoarelor (lucrări de laborator ) 33
операции деления, для которой размер делимого должен быть в два раза больше размера
делителя, либо для приведения операндов к одной размерности в командах умножения,
сложения, вычитания.

Ex. Вычитание двух операндов – 32 бита (dw) и 16 бит(sw).

dw dword 12345678h
sw word 0abcdh
rez dword ?
................
mov ax, so
cwd ; operand находится в паре DX : AX
mov bx, ax
mov ax, word ptr do
subax, bx
mov word ptr rez, ax
mov ax, word ptr do + 2
sbbax, dx ; возможный заем
mov word ptr rez + 2

Команда CDQ (convert doubleword to quadword)


Расширения значения знакового бита из EAX [EAX 31] и заполнение им всего регистра EDX, в
итоге получается 64 битное значение в паре регистров EDX:EAX, т.е.:
Если знак [EAX31] = 0, тогда [EDX]  00000000h,
Если [EAX31] = 1, тогда [edx]  0ffffffffh.

Команда не имеет операндов, выполнение команды не влияет на флаги.

Команда MUL (MULtiply) - Умножение целочисленное без учета знака


Схема команды: mul множитель_1
Назначение: операция умножения двух целых чисел без учета знака.
Алгоритм работы:
Команда выполняет умножение двух операндов без учета знаков. Алгоритм зависит от
формата операнда команды и требует явного указания местоположения только одного
сомножителя, который может быть расположен в памяти или в регистре. Местоположение
второго сомножителя фиксировано и зависит от размера первого сомножителя.
Синтаксис:
MUL reg/mem8
MUL reg/mem16
MUL reg/mem32
MUL reg/mem64

Eсли операнд, указанный в команде имеет разрядность 8/16/32/64 бита, то второй


сомножитель должен располагаться в al/ax/eax/rax.
При умножении операндов разрядностью 8/16/32/64 бита результат помещается в ax, в пару
dx:ax, в пару edx:eax, в пару rdx:rax соответственно.
Состояние флагов после выполнения команды (если старшая половина результата нулевая):
OFSF ZF AF PF CF
0 ? ? ? ? 0
Состояние флагов после выполнения команды (если старшая половина результата ненулевая):
OFSF ZF AF PF CF
1 ? ? ? ? 1

Если умножаются байты:


[AX]  [AL] * [reg/mem8]
Arhitectura calculatoarelor (lucrări de laborator ) 34
mov al,5h
mov bl,10h
mul bl ; AX = 0050h, CF = 0
Следующий рисунок показывает взаимодействие между регистрами:

Если умножаются операнды разрядностью 16 бит:


[DX:AX]  [AX] * [reg/mem16]

.data
val1 WORD 2000h
val2 WORD 0100h
.code
mov ax,val1 ; AX = 2000h
mul val2 ; DX:AX = 00200000h, CF = 1

Следующий рисунок показывает взаимодействие между регистрами:

Если умножаются операнды разрядностью 32 бита:


[EDX:EAX]  [EAX] * [reg/mem32]

mov eax,12345h
mov ebx,1000h
mul ebx ; EDX:EAX = 0000000012345000h, CF = 0

Следующий рисунок показывает взаимодействие между регистрами:

Если умножаются операнды разрядностью 32 бита:

[RDX:RAX]  [RAX] * [reg/mem64]

mov rax,0FFFF0000FFFF0000h
mov rbx,2
mul rbx ; RDX:RAX = 0000000000000001FFFE0001FFFE0000

IMUL (Integer MULtiply) - Умножение целочисленное со знаком.


Назначение: операция умножения двух целочисленных двоичных значений со знаком.
Алгоритм работы:
Алгоритм работы команды зависит от используемой структуры команды.
Структура команды с одним операндом:

IMUL reg/mem8 ; AX = AL * reg/mem8
IMUL reg/mem16 ; DX:AX = AX * reg/mem16
IMUL reg/mem32 ; EDX:EAX = EAX * reg/mem32
Arhitectura calculatoarelor (lucrări de laborator ) 35

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


результат до разрядности регистра приемника. Если теряются старшие биты то
устанавливаются флаги CF и OF.

IMUL reg16,reg/mem16
IMUL reg16,imm8
IMUL reg16,imm16

IMUL reg32,reg/mem32
IMUL reg32,imm8
IMUL reg32,imm32

Структура команды с тремя операндами - op1=op2*op3 (округляет результат):

IMUL reg16,reg/mem16,imm8
IMUL reg16,reg/mem16,imm16
IMUL reg32,reg/mem32,imm8
IMUL reg32,reg/mem32,imm32

Пример с двумя операндами:

.data
word1 SWORD 4
dword1 SDWORD 4
.code
mov ax,­16  ; AX = ­16
mov bx,2  ; BX = 2
imul bx,ax  ; BX = ­32
imul bx,2  ; BX = ­64
imul bx,word1 ; BX = ­256
mov eax,­16  ; EAX = ­16
mov ebx,2  ; EBX = 2
imul ebx,eax ; EBX = ­32
imul ebx,2  ; EBX = ­64
imul ebx,dword1 ; EBX = ­256

Пример с тремя операндами:


.data
word1 SWORD 4
dword1 SDWORD 4
.code
imul bx,word1,­16  ; BX = word1 * ­16
imul ebx,dword1,­16  ; EBX = dword1 * ­16
imul ebx,dword1,­2000000000 ; signed overflow!

DIV (DIVide unsigned) - Деление беззнаковое


Схема команды: div делитель
Назначение: выполнение операции деления двух двоичных беззнаковых значений.
Команда DIV служит для деления на 8-, 16-, 32-, 64- разрядное беззнаковое целое число,
находящееся в одном из регистров общего назначения или в памяти операнда, расположенного в
регистрах АХ, DX:АХ, ЕDХ:ЕАХ или RAX:RDX.
DIV r/m8
DIV r/m16
DIV r/m32
DIV r/m64
Arhitectura calculatoarelor (lucrări de laborator ) 36

Команда DIV имеет всего один операнд, являющийся делителем. В следующей таблице
указано, в каких регистрах размещается делимое, делитель, частное и остаток в зависимости от
размера множителя.

Делимое Делитель Частное Остаток


AX r/m8 AL AH
DX:АХ r/m16 AX DX
ЕDХ:ЕАХ r/m32 ЕАХ ЕDХ
RDX:RAX r/m64 RAX RDX

Если делитель размером в байт, то делимое должно быть расположено в регистре ax. После
операции частное помещается в al, а остаток — в ah:
[al] [ax]/ [делитель]
[ah] остаток [ax]/ [делитель]

mov ax,0083h  ; делимое
mov bl,2  ; делитель
div bl  ; AL = 41h­частное, AH = 01h­остаток

Если делитель размером в слово, то делимое должно быть расположено в паре регистров
dx:ax, причем младшая часть делимого находится в ax.
После операции частное помещается в ax, а остаток — в dx;

[ax] частное [dx:ax]/[делитель]


[dx] остаток [dx:ax]/[ делитель]

mov dx,0 ; clear делимое, high
mov ax,8003h ; делимое, low
mov cx,100h ; делитель
div cx ; AX = 0080h­частное, DX = 0003h­ остаток

Если делитель имеет разрядность 32 бита, тогда делимое находится в регистрах EDX и EAX (64
бита), частное загружается в EAX а остаток в EDX.

.data
dividend QWORD 0000000800300020h
divisor DWORD 00000100h
.code
mov edx,DWORD PTR dividend + 4 ; high doubleword
mov eax,DWORD PTR dividend ; low doubleword
div divisor ; EAX = 08003000h, EDX = 00000020h
Arhitectura calculatoarelor (lucrări de laborator ) 37
Если делитель имеет разрядность 64 бита, тогда делимое находится в регистрах RDX и RAX
(128 бита), частное загружается в RAX а остаток в RDX.

.data
dividend_hi QWORD 0000000000000108h
dividend_lo QWORD 0000000033300020h
divisor QWORD 0000000000010000h
.code
mov rdx,dividend_hi
mov rax,dividend_lo
div divisor  ; RAX = 0108000000003330
; RDX = 0000000000000020

При выполнении операции деления возможно возникновение исключительной ситуации:


0 — ошибка деления. Эта ситуация возникает в одном из двух случаев: делитель равен 0 или
частное слишком велико для его размещения в регистре al/ax/eax/rax.

IDIV (Integer DIVide) - Деление целочисленное со знаком


Схема команды: idiv делитель
Назначение: операция деления двух двоичных значений со знаком.
Команда IDIV позволяет выполнить деление целых чисел со знаком. Она имеет те же
форматы операнда, что и команда DIV. При делении на 8-, 16-, 32-, 64- разрядное число, перед
выполнением команды IDIV нужно расширить знак делимого с помощью одной из команд
CBW, CWD, CDQ в зависимости от разрядности делителя. В приведенном ниже примере
выполняется деление числа -48 на 5. После выполнения команды IDIV в регистре AX будет
находиться частное, равное -9, а в регистре DX -остаток, равный -3:

;деление слов
mov ax,-48 ;делимое
mov bx,5 ;делитель
cwd ;расширение делимого dx:ax
idiv bx ;частное в ax, остаток в dx

Остаток всегда имеет знак делимого. Знак частного зависит от состояния знаковых битов
(старших разрядов) делимого и делителя. При выполнении операции деления возможно
возникновение исключительной ситуации: 0 — ошибка деления. Эта ситуация возникает в
одном из двух случаев: делитель равен 0 или частное слишком велико для его размещения в
регистре rax/eax/ax/al.

2.4 Задания к лабораторной работе


1. Произвести ассемблирование, исполните в пошаговом режиме Debug и
прокомментируйте программу:
INCLUDE Irvine32.inc
.data
alfa WORD 3 DUP(?)
.code
main proc
mov ax,17
mov ax,10101b
mov ax,11b
mov ax,21o
mov ebx,offset alfa
mov alfa,ax
mov cx,ax ; obmen soderjimogo registrov ax i bx
mov ax,bx ; ispolzuia cx
mov ax,cx
xchg ax,bx
Arhitectura calculatoarelor (lucrări de laborator ) 38
mov si,2
mov alfa[si],ax
mov ebx,offset alfa
lea ebx,alfa
mov esi,2
mov cx,[ebx][esi]
mov cx,alfa[2]
mov cx,[alfa+2]
mov edi,4
mov byte ptr [ebx][edi],55h
mov esi,2
mov ebx,3
mov alfa[ebx][esi],33h
mov alfa[ebx+esi],33h
mov [alfa+ebx+esi],33h
mov [ebx][esi]+alfa,33h

exit
main ENDP
END main
2. Вычислить арифметическое выражение: e=((a+b*c-d)/f+g*h)/i. Произвести ассемблирование
и исполнить в пошаговом режиме в Debug.

INCLUDE Irvine32.inc

; vicisliti arifmeticeskoe virajenie: e=((a+b*c-d)/f+g*h)/i


; razreadnosti a, d, f – slovo b, c, g, h, i –byte
; для деления на f необходимо расширить делимое до двойного слова
; использовать только частное, разрядность результата-байт

.data
a dw 5
b db 6
cd db 10
d dw 5
f dw 6
g db 10
h db 11
i db 10
interm dw ?
rez db ?

.code
main proc
mov eax,0
mov al, b
imul cd ; в ax coхранится результат b*c
add ax, a ; ax=b*c+a
sub ax, d ; ax=b*c+a-d
cwd ; расширили слово из ax, в двойное слово в dx:ax
idiv f ; частное в ax и остаток в dx, ax=(a+b*c-d)/f
mov interm, ax ; interm=(a+b*c-d)/f
mov al, g
imul h ; ax=g*h
add ax, interm ; ax=(a+b*c-d)/f+g*h
idiv i ; частное в al и остаток в ah
mov rez, al

exit
Arhitectura calculatoarelor (lucrări de laborator ) 39
main ENDP
END main

; проверка результата ((a+b*c-d)/f+g*h)/i=((5+6*10-5)/6+10*11)/10= 12

2.5 Варианты индивидуальных заданий


Вычислить арифметическое выражение:
1. z=15/(a*a+b*b-5)+24/(a*a-b*b+4)
2. z=a+b*b-(36/(b*b)/(1+(25/(b*b)))
3. z=(3+(c*c))/(6-(b*b))+((a*a-b*b)/(a*a+c*c))
4. z=(a*3+b*b*5)/(a*a+2*a*b)-a-b
5. z=(a+b+c+1) *(a+b+c+1) /((a-b+d)*(a-b+d))
6. z=(a-b*c/d)/(c+2-a/b)+5
7. z=(5*a-b/7)/(3/b+a*a)
8. z=(a+b+c+1)3/(a-b*c+d)2
9. z=((a+1)*(a+1)+2)2/(b*b+c*c)
10. z=(a*a+b*b)/(a*a-b*b-5).
11. z=(5*a-b/7)/(3/b+a*a).
12. z=(2+1/a)/(3+1/(b*b))-1/(c*c)
13. z=((a+b)/c + 2*d)/e
14. z= ((a*c-b*d)/f +(a+b)*c/d)/h
15. z=((a+b*c-d)/f+h)/g

2.6 Содержание отчёта


Отчёт по лабораторной работе должен содержать листинги программ (*.lst) 1 и 2 из п.2.4, а т
акже разработанную прокомментированную программу (*.asm), соответствующей варианту
индивидуального задания из п. 2.5.

2.7 Задачи для неаудиторной работы


Вопросы для подготовки студентов к лабораторной работе 3: Обьясните команды - MOV,
MOVZX, MOVSX, XCHG, XLAT, IN, OUT, LEA, LAHF, SAHF, PUSH, POP, PUSHFD, POPFD,
PUSHAD, PUSHA, POPAD, РОРА, INC, DEC, NEG, CMP, JMP, JE, JZ, JNE, JNZ, JL/JNGE,
JLE/JNG, JG/JNLE, JGE/JNL, JB/JNAE, JBE/JNA, JA/JNBE, JAE/JNB, JCXZ, AAA, AAS, DAS,
AAM.
Arhitectura calculatoarelor (lucrări de laborator ) 40

ЛАБОРАТОРНАЯ РАБОТА №3
ОСНОВЫ ЯЗЫКА АССЕМБЛЕР
(Часть II)
3.1 Цель работы:
Работа рассматривает команды пересылки данных, команды арифметических операций над
целыми двоичными числами и над двоично-десятичными числами.

3.2 Команды пересылки данных


Стандартный формат команды ассемблера x86 состоит из четырех основных частей:

[label:] mnemonic [operands] [;comment]

 необязательной метки;
 мнемоники команды, которая присутствует всегда;
 одного или нескольких операндов (как правило. они присуrствуют в любой команде, хотя
есть ряд команд, для которых операнды не требуются);
 необязательного комментария.

Команды могут содержать ноль, один, два или три операнда. Опустим необязательные части
метки и комментария:

mnemonic
mnemonic [destination]
mnemonic [destination],[source]
mnemonic [destination],[source-1],[source-2]

Есть три основных типа операндов, которые могут встречаться в любой команде:
 непосредственно заданное значение (immediate);
 регистр (register) – значение находится в одном из регистров микропроцессора;
 память (memory).

Команда MOV (MOVe operand) - Пересылка операнда


Схема команды: mov приемник,источник
Назначение: пересылка данных между регистрами или регистрами и памятью.
Алгоритм работы:
копирование второго операнда в первый операнд.
Состояние флагов после выполнения команды: выполнение команды не влияет на флаги.
В команде моv могут использоваться самые разные операнды. Кроме того, необходимо
учитывать следующие правила и ограничения:
 Оба операнда должны иметь одинаковую длину.
 В качестве одного из операндов обязательно должен использоваться регистр
(т.е.пересылки типа "память-память" в команде моv не поддерживаются).
 В качестве приемника нельзя указывать регистры CS, IP, ЕIР или RIP.
 Нельзя переслать непосредственно заданное значение в сегментный регистр.
Ниже приведены варианты использования команды моv с разными операндами (кроме
сегментных регистров):
MOV reg,reg
MOV mem,reg
MOV reg,mem
MOV mem,imm
MOV reg, imm

Примеры:
Arhitectura calculatoarelor (lucrări de laborator ) 41
.data
var1 WORD ?
var2 WORD ?
.code
mov ax,var1
mov var2,ax

.data
oneByte BYTE 78h
oneWord WORD 1234h
oneDword DWORD 12345678h
.code
mov eax,0 ; EAX = 00000000h
mov al,oneByte ; EAX = 00000078h
mov ax,oneWord ; EAX = 00001234h
mov eax,oneDword ; EAX = 12345678h
mov ax,0 ; EAX = 12340000h

.data
alfa dw 1234h
beta db 56h
.code
mov bl,al
mov bx,ds
mov ax, alfa; пересылка содержимого по смещению alfa в аx
mov bx, offset beta; пересылка эффективного адреса beta в bx
mov al, 75h;
mov cx, [100]; пересылка содержимого адреса 100 в cx
mov [di], bx; пересылка содержимого регистра bx по адресу в регистре di
mov byte ptr alfa , [bx]; пересылка содержимого по адресу в регистре bx в байт по
; смещению alfa

Команда MOVZX (Move With Zero-Extend, или переместить и дополнить нулями)


копирует содержимое исходного операнда в больший по размеру регистр приемник данных.
При этом оставшиеся неопределенными биты регистра-приемника (как правило, старшие 16
или 24 бита) сбрасываются в ноль. Эта команда используется только при работе с беззнаковыми
целыми числами. Существует три варианта команды мovzx:

MOVZX reg32,reg/mem8
MOVZX reg32,reg/mem16
MOVZX reg16,reg/mem8

Примеры:
.data
byteVal BYTE 10001111b
.code
movzx ax,byteVal ; AX = 0000000010001111b
Arhitectura calculatoarelor (lucrări de laborator ) 42

.data
byte1 BYTE 9Bh
word1 WORD 0A69Bh
.code
movzx eax,word1 ; EAX = 0000A69Bh
movzx edx,byte1 ; EDX = 0000009Bh
movzx cx,byte1 ; CX = 009Bh

Команда MOVSX (Move With Sign-Extend, или Переместить и дополнить знаком)


копирует содержимое исходного операнда в больший по размеру регистр получателя данных,
также как и команда мovzx. При этом оставшиеся неопределенными биты регистра-получателя
(как правило, старшие 16 или 24 бита) заполняются значением знакового бита исходного
операнда. Эта команда используется только при работе со знаковыми целыми числами.
Существует три варианта команды MOVSX:
MOVZX reg32,reg/mem8
MOVZX reg32,reg/mem16
MOVZX reg16,reg/mem8

Пример:

mov bx,0A69Bh
movsx eax,bx ; EAX = FFFFA69Bh
movsx edx,bl ; EDX = FFFFFF9Bh
movsx cx,bl ; CX = FF9Bh

Команда XCHG (Exchange Data, или Обмен данными) позволяет обменять содержимое
двух операндов. Существует три варианта команды XCHG:
XCHG reg, reg
XCHG reg,mem
XCHG mem,reg
Для операндов команды XCHG нужно соблюдать те же правила и ограничения, что и для
операндов команды MOV, за исключением того, что операнды команды XCHG не могут быть
непосредственно заданными значениями.

Пример:
xchg al, ah
xchg alfa, ax
xchg sir [si], bx
xchg eax,ebx ; exchange 32-bit regs

Чтобы поменять содержимое двух переменных, расположенных в памяти, необходимо


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

mov reg, op1


xchg reg, op2
mov op2, reg
Arhitectura calculatoarelor (lucrări de laborator ) 43

Пример программы:
.data
val1 WORD 1000h
val2 WORD 2000h
arrayB BYTE 10h,20h,30h,40h,50h
arrayW WORD 100h,200h,300h
arrayD DWORD 10000h,20000h
.code
main PROC
; Demonstrating MOVZX instruction:
mov bx,0A69Bh
movzx eax,bx ; EAX = 0000A69Bh
movzx edx,bl ; EDX = 0000009Bh
movzx cx,bl ; CX = 009Bh
; Demonstrating MOVSX instruction:
mov bx,0A69Bh
movsx eax,bx ; EAX = FFFFA69Bh
movsx edx,bl ; EDX = FFFFFF9Bh
mov bl,7Bh
movsx cx,bl ; CX = 007Bh
; Memory-to-memory exchange:
mov ax,val1 ; AX = 1000h
xchg ax,val2 ; AX=2000h, val2=1000h
mov val1,ax ; val1 = 2000h
; Direct-Offset Addressing (byte array):
mov al,arrayB ; AL = 10h
mov al,[arrayB+1] ; AL = 20h
mov al,[arrayB+2] ; AL = 30h
; Direct-Offset Addressing (word array):
mov ax,arrayW ; AX = 100h
mov ax,[arrayW+2] ; AX = 200h
; Direct-Offset Addressing (doubleword array):
mov eax,arrayD ; EAX = 10000h
mov eax,[arrayD+4] ; EAX = 20000h
mov eax,[arrayD+4] ; EAX = 20000h

XLAT (transLATe Byte from table) - Преобразование байта


Схема команды: xlat адрес_таблицы_байтов
Назначение: подмена байта в регистре al байтом из последовательности (таблицы) байтов в
памяти.
Алгоритм работы:
 вычислить адрес, равный ds:bx+(al);
 выполнить замену байта в регистре al байтом из памяти по вычисленному адресу.
Несмотря на наличие операнда адрес_таблицы_байтов в команде xlat, адрес
последовательности байтов, из которой будет осуществляться выборка байта для подмены в
регистре al, должен быть предварительно загружен в пару ds:bx. Команда xlat допускает замену
сегмента.
Состояние флагов после выполнения команды: выполнение команды не влияет на флаги
Применение:
Команду xlat можно использовать для выполнения перекодировок символов. Для
формирования адреса таблицы в регистрах bx можно использовать команду lea или оператор
ассемблера offset в команде mov.
Arhitectura calculatoarelor (lucrări de laborator ) 44
Пример:
table db 'abcdef'
int db 0 ;значение индекса
...
mov al,3
lea bx,table
xlat ;(al)='c'

IN (INput operand from port) - Ввод операнда из порта


Схема команды: in аккумулятор,ном_порта
Назначение: ввод значения из порта ввода-вывода.
Алгоритм работы:
Передает байт, слово, двойное слово из порта ввода-вывода в один из регистров al/ax/eax.
Состояние флагов после выполнения команды: выполнение команды не влияет на флаги.
Применение:
Команда применяется для прямого управления оборудованием компьютера посредством
портов. Номер порта задается вторым операндом в виде непосредственного значения или
значения в регистре dx. Непосредственным значением можно задать порт с номером в
диапазоне 0-255. При использовании порта с большим номером используется регистр dx. Размер
данных определяется размерностью первого операнда и может быть байтом, словом, двойным
словом.

OUT (OUT operand to port) - Вывод операнда в порт


Схема команды: out ном_порта,аккумулятор
Назначение: вывод значения в порт ввода-вывода.
Алгоритм работы:
Передать байт, слово, двойное слово из регистра al/ax/eax в порт, номер которого
определяется первым операндом.
Состояние флагов после выполнения команды: выполнение команды не влияет на флаги
Применение:
Команда применяется для прямого управления оборудованием компьютера посредством
портов. Номер порта задается первым операндом в виде непосредственного значения или
значения в регистре dx. Непосредственным значением можно задать порт с номером в
диапазоне 0...255. Для указания порта с большим номером используется регистр dx. Размер
данных определяется размерностью второго операнда и может быть байтом, словом или
двойным словом.
out 64h,al

Пример:
in al,3Ch ; input byte from port 3Ch
out 3Ch,al ; output byte to port 3Ch
mov dx, portNumber ; DX can contain a port number
in ax,dx ; input word from port named in DX
out dx,ax ; output word to the same port
in eax,dx ; input doubleword from port
out dx,eax ; output doubleword to same port

LEA (Load Effective Address) - Загрузка эффективного адреса


Схема команды: lea приемник,источник
Назначение: получение эффективного адреса (смещения) источника.
Алгоритм работы:
в регистр приемник загружается 16-битное значение смещения операнда источник;
Состояние флагов после выполнения команды: выполнение команды не влияет на флаги
Применение:
Arhitectura calculatoarelor (lucrări de laborator ) 45
Данная команда является альтернативой оператору ассемблера offset. В отличие от offset
команда lea допускает индексацию операнда, что позволяет более гибко организовать
адресацию операндов.
;загрузить в регистр bx адрес пятого элемента массива mas
.data
mas db 10 dup (0)
.code
...
mov di,4
lea bx,mas[di]
;или
lea bx,mas[4]
;или
lea bx,mas+4

Команды LAHF и SAHF


Команда LAHF (Load Status Flags lnto АН, или загрузить флаги состояния в регистр АН)
позволяет загрузить в регистр АН младший байт регистра флагов EFLAGS. При этом в регистр
АН копируются следующие флаги состояния: SF (флаг знака), ZF, (флаг нуля), AF (флаг
служебного переноса), PF (флаг четности) и CF (флаг переноса). С помошью этой команды
можно легко сохранить содержимое регистра флагов в переменной для дальнейшего анализа:

.data
saveflags ВУТЕ ?
.code
lahf ; Загрузить флаги в регистр АН
mov saveflags,ah ; Сохранить флаги в переменной

Команда SAHF (Store АН lnto Status Flags, или записать регистр АН во флаги) помещает
содержимое регистра АН в младший байт регистра флагов EFLAGS. Например, вы можете
восстановить сохраненное ранее в переменной значение флагов:

тоv ah,saveflags ; загрузим в регистр АН сохраненное ранее


; значение регистра флагов
sahf ; скопируем его в младший байт регистра EFLAGS

Команды PUSH и РОР


Команда PUSH помешает в стек значение 16- или 32-разрядноrо операнда, уменьшая перед
этим значение регистра ESР, соответственно, на 2 или 4. Существует три формата команды
PUSH:

PUSH r/m16
PUSH r/m32
PUSH imm16/ imm32

Команда РОР
Команда РОР копирует содержимое вершины стека, на которую указывает регистр ESP, в
16- или 32-разрядный операнд, указанный в команде, а затем прибавляет к регистру ESP,
соответственно, число 2 или 4. Существует два формата команды РОР:

РОР r/m16
РОР r/m32

Команды PUSHFD и POPFD


Arhitectura calculatoarelor (lucrări de laborator ) 46
Команда PUSHFD помещает в стек значение 32-разрядного регистра флагов процессора
EFLAGS, а команда POPFD выполняет обратную операцию, т.е. восстанавливает значение
регистра ЕFLAGS из стека. У этих команд операндов нет:

pushfd
popfd

В реальном режиме для сохранения в стеке значения 16-разрядного регистра флагов


FLAGS используется команда PUSHF, а для выполнения обратной операции - команда POPF.
Пример:
.data
saveFlags DWORD ?
.code
pushfd ; Сохранить в стеке регистр флагов EFLAGS
рор saveFlags ; Скопировать значение регистра
;флагов EFLAGS из стека в переменную

Команды PUSHAD, PUSHA, POPAD и РОРА


Команда PUSHAD сохраняет в стеке значение всех 32-разрядных регистров общего
назначения в следующем порядке: ЕАХ, ЕСХ, EDX, ЕВХ, ESP (то значение, которое было до
выполнения команды), ЕВР, ESI и EDI. Команда POPAD выполняет обратную операцию, т.е.
восстанавливает из стека значения указанных регистров в обратном порядке. По аналогии,
команда PUSHA сохраняет в стеке значение всех 16-разрядных регистров общего назначения в
следующем порядке: АХ, СХ, ОХ, ВХ, SP (то значение, которое было до выполнения команды),
ВР, SI и DI. Команда РОРА выполняет обратную операцию, т.е. восстанавливает из стека
значения указанных регистров в обратном порядке.
Команда PUSHAD обычно используется в начале процедуры или фрагмента кода, в
котором модифицируется много 32-разрядных регистров общего назначения. Для
восстановления первоначального значения этих регистров в конце процедуры или фрагмента
кода используется команда POPAD. Ниже приведен фрагмент кода:

MySub PROC
pushad ; Сохраним в стеке регистры общего назначения
.
.
mov еах, … .
mov edx, … .
mov есх, … .
.
.
popad ; Восстановим значения регистров
ret
MySub ENDP

INC (INCrement operand by 1) - Увеличить операнд на 1


Схема команды: inc операнд
Назначение: увеличение значения операнда в памяти или регистре на 1.
Алгоритм работы: команда увеличивает операнд на единицу.
Выполнение команды влияет на состояние флагов: OF SF ZF AF PF
Применение:
Команда используется для увеличения значения байта, слова, двойного слова в памяти
или регистре на единицу. При этом команда не воздействует на флаг cf.
Ex:
inc alfa
inc bl
inc eax
Arhitectura calculatoarelor (lucrări de laborator ) 47
inc rbx
inc word ptr [bx] [si]

DEC (DECrement operand by 1) - Уменьшение операнда на единицу


Схема команды: dec операнд
Назначение: уменьшение значения операнда в памяти или регистре на 1.
Алгоритм работы: команда вычитает 1 из операнда.
Выполнение команды влияет на состояние флагов: OF SF ZF AF PF
Применение:
Команда dec используется для уменьшения значения байта, слова, двойного слова в
памяти или регистре на единицу. При этом заметьте то, что команда не воздействует на флаг cf.
mov al,9
...
dec al ;al=8

NEG (NEGate operand) - Изменить знак операнда


Схема команды: neg источник
Назначение: изменение знака (получение двоичного дополнения) источника.
Синтаксис
Алгоритм работы:
 выполнить вычитание (0 – источник) и поместить результат на место источника;
 если источник=0, то его значение не меняется.
Состояние флагов после выполнения команды (если результат нулевой):
OF SF ZF AF PF CF
r r r r r 0
Состояние флагов после выполнения команды (если результат ненулевой):
OF SF ZF AF PF CF
r r r r r 1
Применение:
Команда используется для формирования двоичного дополнения операнда в памяти или
регистре. Операция двоичного дополнения предполагает инвертирование всех разрядов
операнда с последующим сложением операнда с двоичной единицей. Если операнд
отрицательный, то операция neg над ним означает получение его модуля.

mov al,2
neg al ;al=0feh — число -2 в дополнительном коде

CMP (CoMPare operands) - Сравнение операндов


Схема команды: cmp операнд1,операнд2
Назначение: сравнение двух операндов.
Алгоритм работы:
 выполнить вычитание (операнд1-операнд2);
 в зависимости от результата установить флаги, операнд1 и операнд2 не изменять (то есть
результат не запоминать).
Выполнение команды влияет на состояние флагов: OF SF ZF AF PF CF
Применение:
Данная команда используется для сравнения двух операндов методом вычитания, при этом
операнды не изменяются. По результатам выполнения команды устанавливаются флаги Zero и
Carry в соответствии с таблицей:

Команда cmp применяется с командами условного перехода.


Arhitectura calculatoarelor (lucrări de laborator ) 48
len equ 10
...
cmp ax,len
jne m1 ;переход если (ax)<>len
jmp m2 ;переход если (ax)=len

3.3 Команды переходов, управления циклом

JMP (JuMP) Переход безусловный


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

Синтаксис команды: jmp метка_перехода


Назначение: используется в программе для организации безусловного перехода как внутри
текущего сегмента команд, так и за его пределы.
Переход может быть:
 прямым коротким (short) (в пределах -128... + 127 байтов);
 прямым ближним (near) (в пределах текущего сегмента команд);
 прямым дальним (far) (в другой сегмент команд);
 косвенным ближним (в пределах текущего сегмента команд через ячейку с адресом
перехода).

Команды условных переходов. Микропроцессор имеет 18 команд условного перехода. Эти


команды позволяют проверить:
 отношение между операндами со знаком (“больше — меньше”);
 отношение между операндами без знака (“выше — ниже”);
 состояния арифметических флагов zf, sf, cf, of, pf (но не af).
Команды условного перехода имеют одинаковый синтаксис:
jcc метка_перехода
Как видно, мнемокод всех команд начинается с “j” — от слова jump (прыжок), cc —
определяет конкретное условие, анализируемое командой.
Что касается операнда метка_перехода, то эта метка может находится только в пределах
текущего сегмента кода, межсегментная передача управления в условных переходах не
допускается.

Таблица 3.1 - Значение аббревиатур в названии команды jcc


Мнемоническое обозначение Английский Русский Тип операндов
Ee equal Равно Любые
Nn not Не Любые
Gg greater Больше Числа со знаком
Ll less Меньше Числа со знаком
Aa above Выше, в смысле “больше” Числа без знака
Bb below Ниже, в смысле “меньше” Числа без знака

Таблица 3.2 - Перечень команд условного перехода для команды cmp


Значения флагов
Типы Мнемокод команды
Критерий условного перехода для осществления
операндов условного перехода
перехода
Любые je операнд_1 = операнд_2 zf = 1
Любые jne операнд_1<>операнд_2 zf = 0
Со знаком jl/jnge операнд_1 < операнд_2 sf <> of
Со знаком jle/jng операнд_1 <= операнд_2 sf <> of or zf = 1
Со знаком jg/jnle операнд_1 > операнд_2 sf = of and zf = 0
Arhitectura calculatoarelor (lucrări de laborator ) 49
Со знаком jge/jnl операнд_1 => операнд_2 sf = of
Без знака jb/jnae операнд_1 < операнд_2 cf = 1
Без знака jbe/jna операнд_1 <= операнд_2 cf = 1 or zf=1
Без знака ja/jnbe операнд_1 > операнд_2 cf = 0 and zf = 0
Без знака jae/jnb операнд_1 => операнд_2 cf = 0
Можно заметить, что одинаковым значениям флагов соответствует несколько разных
мнемокодов команд условного перехода (они отделены друг от друга косой чертой в табл. 3.2).
Разница в названии обусловлена желанием разработчиков микропроцессора облегчить
использование команд условного перехода в сочетании с определенными группами команд.
Поэтому разные названия отражают скорее различную функциональную направленность. Тем
не менее, то, что эти команды реагируют на одни и те же флаги делает их эквивалентными и
равноправными в программе. Поэтому в табл. 3.2 они сгруппированы не по названиям, а по
значениям флагов (условиям), на которые они реагируют.

Примеры: mov ax,0FFFEh


mov bx, 2
cmp ax, bx
ja alfa; jump is taken

и
mov ax, 0FFFEh
mov bx, 2
cmp ax, bx
jg alfa; jump not taken
В первом примере переход на метку alfa произойдет, во втором нет.

Команда JCXZ (Jump if CX=Zero)


Переход, если CX равен нулю
JCXZ метка_перехода
JECXZ метка_перехода
Синтаксис команды:
JRCXZ метка_перехода

Назначение: переход внутри текущего сегмента команд (от -128 до 127) в зависимости от
некоторого условия.
\Алгоритм работы команды jcxz:
Проверка условия равенства нулю содержимого регистра cx/ecx/rcx:
 если проверяемое условие истинно, то есть содержимое cx/ecx/rcx равно 0, то перейти к
ячейке, обозначенной операндом метка;
 если проверяемое условие ложно, то есть содержимое cx/ecx/rcx не равно 0, то передать
управление следующей за jxxz команде программы.
Применение:
Например, команду можно использовать для предварительной проверки счетчика цикла в
регистре cx/ecx/rcx для обхода цикла, если его счетчик нулевой.
Примеры:
mov edx,0A523h
cmp edx,0A523h
jne L5 ; jump not taken
je L1 ; jump is taken

mov bx,1234h
sub bx,1234h
jne L5 ; jump not taken
je L1 ; jump is taken

mov cx,0FFFFh
Arhitectura calculatoarelor (lucrări de laborator ) 50
inc cx
jcxz L2 ; jump is taken

xor ecx,ecx
jecxz L2 ; jump is taken

сравнение со знаком:
mov edx,­1
cmp edx,0
jnl L5 ; jump not taken (­1>=0 is false)
jnle L5 ; jump not taken (­1 > 0 is false)
jl L1 ; jump is taken (­1 < 0 is true)

mov bx,+32
cmp bx,­35
jng L5 ; jump not taken (+32 <= ­35 is false)
jnge L5 ; jump not taken (+32 < ­35 is false)
jge L1 ; jump is taken (+32 >= ­35 is true)

mov ecx,0
cmp ecx,0
jg L5 ; jump not taken (0 > 0 is false)
jnl L1 ; jump is taken (0 >= 0 is true)

mov ecx,0
cmp ecx,0
jl L5 ; jump not taken (0 < 0 is false)
jng L1 ; jump is taken (0 <= 0 is true)

Команда LOOP (LOOP control by register еcx) - Управление циклом по еcx.


Команда LOOP позволяет выполнить некоторый блок команд заданное количество раз. В
качестве счетчика используется регистр ЕСХ, значение которого автоматически уменьшается на
единицу при каждом выполнении команды LOOP. Синтаксис этой команды следующий:
loop метка
Назначение: организация цикла со счетчиком в регистре еcx.
Алгоритм работы:
 выполнить декремент содержимого регистра еcx;
 анализ регистра еcx:
 если еcx=0, передать управление следующей за loop команде;
 если еcx=1, передать управление команде, метка которой указана в качестве операнда
loop.
Также могут быть применены команды с синтаксисами:
 Команда LOOPD использует регистр ECX как регистр счетчик;
 Команда LOOPW использует регистр CX как регистр счетчик.

Применение: Команду loop применяют для организации цикла со счетчиком. Количество


повторений цикла задается значением в регистре еcx перед входом в последовательность
команд, составляющих тело цикла. Помните о двух важных моментах:
 для предотвращения выполнения цикла при нулевом cx используйте команду jcxz. Если
этого не сделать, то при изначально нулевом cx цикл повторится 65 536 раз;
Arhitectura calculatoarelor (lucrări de laborator ) 51
 смещение метки, являющейся операндом loop, не должно выходить из диапазона
-128...+127 байт. Это смещение, как и в командах условного перехода, является
относительным от значения счетчика адреса следующей за loop команды.
Ex: Сума n байт с адреса string.

.data
string byte 7, 9, 15, 25, -18, 33, 11
n equ LENGTHOF string
suma byte ?
.code

xor eax, eax


mov ecx, n
xor esi, esi
m1:
add al, string[esi]
inc esi
LOOP m1
mov suma, al
Команды LOOPE/LOOPZ и LOOPNE/LOOPNZ
Команда LOOPZ (Loop if Zero, или цикл пока нуль) позволяет организовать цикл, который
будет выполнятся, пока установлен флаг нуля ZF и значение регистра ЕСХ, взятое без знака,
больше нуля. Метка перехода, указанная в этой команде, должна находиться в пределах
-128 ... +127 байтов относительно адреса следующей команды. Синтаксис команды LOOPZ
следующий:
LOOPZ метка_ перехода

Команда LOOPE (Loop if Equal, или цикл пока равно) полностью аналогична команде
LOOPZ, поскольку в ней учитывается значение того же флага состояния процессора. Ниже
приведена логика работы команд LOOPZ и LOOPE:

ЕСХ = ЕСХ - 1
Если (ЕСХ > О и ZF = 1), то перейти по метке
; Иначе управление переходит следующей команде.

Команда LOOPNZ (Loop if Not Zero, или цикл пока не нуль) по сути аналогична команде
LOOPZ за одним небольшим исключением. Цикл на основе команды LOOPNZ будет
выполняться, пока значение регистра ЕСХ, взятое без знака, больше нуля и сброшен флаг
нуля ZF. Синтаксис команды LOOPNZ следующий:

LOOPNZ метка_перехода

Команда LOOPNE (Loop if Not Equal, или цикл пока не равно) полностью аналогична
команде LOOPNZ, поскольку в ней учитывается значение того же флага состояния
процессора. Ниже приведена логика работы команд LООРNZ и LOOPNE:

ЕСХ = ЕСХ - 1
Если (ЕСХ > О и ZF = О} , то перейти по метке
; Иначе управление переходит следующей команде.

3.4 Команды арифметических операций над двоично-десятичными числами


Данные команды влияют на состояние регистра eflags.
AAA (Ascii Adjust after Addition) - ASCII-коррекция после сложения
Схема команды: aaa
Arhitectura calculatoarelor (lucrări de laborator ) 52
Назначение: корректировка неупакованного результата сложения двух одноразрядных
неупакованных BCD-чисел.
Алгоритм работы:
проанализировать значение младшего полубайта регистра al и значение флага af;
если (значение младшего полубайта регистра al >9) или (AF=1), то выполнить следующие
действия:
 увеличить значение al на 6;
 очистить старший полубайт регистра al;
 увеличить значение ah на 1;
 установить флаги: af = 1, cf = 1,
иначе сбросить флаги af = 0 и cf = 0.
Состояние флагов после выполнения команды:
OFSF ZF AF PF CF
? ? ? r ? r
Применение:
Обычно команда aaa используется после сложения каждого разряда распакованных BCD-
чисел командой add. Каждая цифра неупакованного BCD-числа занимает младший полубайт
байта. Если результат сложения двух одноразрядных BCD-чисел больше 9, то число в младшем
полубайте результата не есть BCD-число. Поэтому результат нужно корректировать командой
aaa. Эта команда позволяет сформировать правильное BCD-число в младшем полубайте и
запомнить единицу переноса в старший разряд путем увеличения содержимого регистра ah на
1.

К примеру, сложить два неупакованных BCD-числа: 48 + 29:


mov ax, 408h
mov dx, 209h
add ax, dx ; [ax]=0611h не неупакованное BCD-число
AAA ; [ax]=0707h— результат скорректирован

AAS (Ascii Adjust after Substraction) - ASCII-коррекция после вычитания


Схема команды: aas
Назначение: корректировка результата вычитания двух неупакованных одноразрядных
BCD-чисел.
Алгоритм работы:
если [AL0:3] > 9 или [AF] = 1, тогда {
[AL]  [AL] - 6
[AH]  [AH] - 1
[AF] 1
[CF] 1
[AL]  [AL] AND 0FH
}
Состояние флагов после выполнения команды:
OFSF ZF AF PF CF
? ? ? r ? r
Пример. Вычесть десятичные числа 29 из 48.
mov ax, 408h
mov dx, 209h
sub ax, dx ; [ax]=01ffh
AAS ; [ax]=0109h— результат скорректирован

DAS (Decimal Adjust for Subtraction) - Десятичная коррекция после вычитания


Схема команды: das
Назначение: коррекция упакованного результата вычитания двух BCD-чисел в упакованном
формате.
Алгоритм работы: команда das работает только с регистром al.
Arhitectura calculatoarelor (lucrări de laborator ) 53
если [AL0:3] > 9 или [AF] = 1, тогда {
[AL]  [AL] - 6
[AF]  1
}
если [AL4:7] > 9 или CF = 1, тогда {
[AL]  [AL] - 60H
[CF]  1
}

Пример. MOV AL, 52H


SUB AL, 24H ; AL = 2EH— не BCD-число
DAS ; AL = 28H— результат скорректирован

AAM (Ascii Adjust after Multiply) - ASCII-коррекция после умножения


Схема команды: aam
Назначение:
 корректировка результата умножения двух неупакованных BCD-чисел;
 преобразование двоичного числа меньшего 63h (9910) в его неупакованный BCD-
эквивалент.
Алгоритм работы:
[AH]  [AL] / 10
[AL]  [AL] MOD 10
Состояние флагов после выполнения команды:
OFSF ZF AF PF CF
? r r r r ?

Пример 1. Умножить десятичные числа 8 на 9.

mov ah,08h ;ah=08h


mov al,09h ;al= 09h
mul ah ;al=48h — не неупакованное
aam ;ah=07h,al=02h— результат скорректирован

Пример 2. Преобразовать двоичное число 60h в эквивалентное десятичное число.

;поместим число 60h в регистр ax


mov ax,60h ;ax=60h
aаm ;ax=0906h — получили десятичный эквивалент(96) числа 60h

AAD (Ascii Adjust before Division) - ASCII-коррекция перед делением.


Схема команды: aad
Назначение:
 подготовкa двух неупакованных BCD-чисел для операции деления.
Алгоритм работы:
[AL]  [AH] * 10 + [AL]
[AH]  0
Состояние флагов после выполнения команды:
OFSF ZF AF PF CF
? r r r r ?

Пример. Разделить десятичное число 35 на 2.


mov ax, 305h
mov bl, 2
AAD ; [ax]=35h
div bl ; [al]=12h [ah]=1
Arhitectura calculatoarelor (lucrări de laborator ) 54

3.5 Рассмотрим несколько примеров


Пример 1. Вычислить арифметические выражения:

Необходимо получить исполняемый файл и исполнить в пошаговом режиме в Debug. Данные


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

INCLUDE Irvine32.inc
.data
mes1 byte "Enter the X:",0
mes2 byte "Enter the Y:",0
mes3 byte "Result:",0
vrx dword 0
vry dword 0
rez dd 0

.code
main PROC
mov edx,OFFSET mes1
call WriteString ; вввод mes1
call ReadDec ; ввод с клавиатуры
mov vrx,eax ; сохранение в переменную vrx

mov edx,OFFSET mes2


call WriteString ; вывод на экран mes2
call ReadDec ; ввод с клавиатуры 2-го значения
mov vry,eax ; сохранение в переменную vry

;проверяем условия
xor eax,eax
mov edx,0

mov eax,vry
mov bx,2
mul bx ; Y*2
cmp vrx,eax ;сравнение X с 2Y
jb con1 ; переход к метке con1, если X<2Y

mov eax,vry ; 2Y-60


mov bx,2
mul bx
sub eax,60
mov rez,eax
jmp ex ; безусловный переход к ex

con1: mov eax,vrx ; X/8+32-Y


mov bx,8
div bx
add eax,32
sub eax,vry
mov rez,eax

ex: mov edx,OFFSET mes3


Arhitectura calculatoarelor (lucrări de laborator ) 55
call WriteString ; вывод mes3
call WriteInt ; вывод результата со знаком
call Crlf ; с новой строки

exit
main ENDP
END main

Пример 2. Есть три целых числа (абс) определенные как три последовательных слова.
Необходимо произвести циклический сдвиг этих чисел влево (бса).
.DATA
vect WORD 100h,200h,300h
.CODE
main PROC
mov dx,vect ; запись в dx первого слова переменной vect
mov ax,vect+2; запись 2-го слова
mov vect,ax ; по адресу первого
mov ax,vect+4; запись 3-го слова
mov vect+2,ax; по адресу второго
mov vect+4,dx; запись 1-го слова по адресу 3-го
exit
main ENDP
END main

Пример 3. Есть три слова в памяти. Необходимо записать значение 0ааh в старшие байты
этих слов.

.DATA
val EQU 0aah
vect DW 111h,222h,333h
.CODE
main PROC
mov al,val ; записывается значение 0aah
; в регистр al
mov BYTE PTR vect[1],al ; запись в
mov BYTE PTR vect[3],al ; старшие
mov BYTE PTR vect[5],al ; байты
exit
main ENDP
END main

Пример 4. Дано 2 числа по 4 цифры в упакованном BCD формате (одно число два байта).
Необходимо разработать программу, исполняющую суммирование и вычитания этих чисел.

.DATA
bcd1 BYTE 34h,18h; 1834 в упакованном BCD формате
bcd2 BYTE 89h,27h; 2789 в упакованном BCD формате
sum BYTE 2 DUP(?)
dif BYTE 2 DUP(?)

.CODE
main PROC
mov al,bcd1
add al,bcd2
daa
Arhitectura calculatoarelor (lucrări de laborator ) 56
mov sum,al
mov al,bcd1+1
adc al,bcd2+1
daa
mov sum+1,al
mov al,bcd1
sub al,bcd2
das
mov dif,al
mov al,bcd1+1
sbb al,bcd2+1
das
mov sum+1,al
exit
main ENDP
END main

Пример 5. Разработать программу умножения двух положительных чисел в неупакованном


BCD формате.

.DATA
npk1 DB 09h ; 9 в неупакованном BCD формате
npk2 DB 03h,02h; 23 в неупакованном BCD формате
rez DB 3 DUP(?)
.CODE
main PROC

mov dl,npk1
mov al,npk2
mul dl
aam
mov dh,ah
mov rez,al
mov al,npk2+1
mul dl
aam
add al,dh
aaa
mov rez+1,al
mov rez+2,ah
exit
main ENDP
END main

3.6 Задание к лабораторной работе


Необходимо выполнить ассемблирование и исполнить вышеприведенные примеры (п. 3.5) в
пошаговом режиме в Debug.

3.7 Варианты индивидуальных заданий


В соответствии с вариантом индивидуального задания разработайте 2 варианта программ:
1) ввод данных с клавиатуры и вывод результата на экран.
2) с генерацией входных данных, используя процедуры Randomize, Random32,
RandomRange.

Nr Арифметические выражения Nr Арифметические выражения


Arhitectura calculatoarelor (lucrări de laborator ) 57
1 9

2 10

3 11

4 12

5 13

6 14

7 15

3.8 Содержание отчета


Отчёт по лабораторной работе должен содержать программы примеров (*.asm) п. 3.5, а т
акже разработанные программы индивидуального задания из п. 3.7.

3.9 Задачи для неаудиторной работы


Вопросы для подготовки к лабораторной работе 4: Объясните разницу между процедурой и
макро процедурой, обьясните команды – CALL, RET, линейного сдвига (SHL, SAL, SHR, SAR)
и циклического сдвига (ROL, RCL, ROR, RCR), команды - SHLD/SHRD, NOT, AND, OR, XOR,
TEST, BSF, BSR, BT, BTC, BTR, BTS.
Arhitectura calculatoarelor (lucrări de laborator ) 58

ЛАБОРАТОРНАЯ РАБОТА №4
ПРОЦЕДУРЫ И МАКРОСЫ
4.1 Цель работы:
Работа рассматривает процедуры и макрокоманды, команды сдвига, логические команды и
несколько примеров их применения.

4.2 Процедуры и макрокоманды


Процедуру можно определить, как блок команд, оканчивающийся оператором возврата.
Для объявления процедуры используются директивы PROC и ENDP. При объявлении процедуре
должно быть назначено имя, которое является одним из разрешенных идентификаторов. При
создании процедур, не являющихся стартовыми, вам нужно в их конце разместить команду RET.
В результате процессор вернет управления команде, следующей за той, которая вызвала эту
процедуру:
sample PROC

ret
sample ENDP

Давайте создадим процедуру SumOf, вычисляющую сумму трех 32-разрядных чисел.


Предположим, что перед вызовом процедуры значения этих чисел мы должны поместить в
регистры ЕАХ, ЕВХ и ЕСХ, а сумма возвращается в регистре ЕАХ:

SumOf PROC
add еах,еbх
add еах,есх
ret
SumOf ENDP

Команды CALL и RET


Команда CALL предназначена для передачи управления процедуре, адрес которой
указывается в качестве параметра. При этом процессор начинает выполнять команду,
расположенную по указанному адресу. Чтобы вернуть управление команде, расположенной
сразу за CALL, в процедуре используется команда RET. Команда CALL помешает в стек
текушее значение счетчика команд, который на фазе выполнения команды CALL содержит
адрес следующей команды, а затем загружает в счетчик команд указанный адрес процедуры.
При возврате из процедуры (т.е. при выполнении в ней команды RET), адрес возврата
загружается из стека в счетчик команд. Напомним, что процессор всегда выполняет команду,
адрес которой указывается в регистре EIP, т.е. в счетчике команд. В 16-разрядном режиме
работы в качестве счетчика команд используется регистр IР.
Arhitectura calculatoarelor (lucrări de laborator ) 59
Пример вызова и возврата из процедуры. Предположим, что в процедуре main по
смещению 00000020h расположена команда CALL. В 32-разрядном режиме длина этой команды
составляет 5 байтов. Поэтому следующая команда (в нашем случае MOV) будет расположена со
смещением 00000025h:

main PROC
00000020 call MySub
00000025 mov еах,еbх

Далее, предположим, что первая команда процедуры МySub расположена со смещением


00000040h:
MySub PROC
00000040 mov eax,edx

ret
MySub ENDP

При выполнении команды CALL в стек помещается адрес следующей за ней команды (в
данном случае 00000025h), после чего в регистр EIP загружается адрес процедуры МуSuB, как
показано ниже

После этого процессор начинает выполнять последовательность команд процедуры МуSuB,


пока в ней не встретится команда RET. При выполнении команды RET содержимое стека, на
которое указывает регистр ESP, копируется в регистр EIP. В результате процессор после
команды RET будет выполнять не следующую за ней команду, а команду, находящуюся по
адресу 00000025h. А это как раз команда, расположенная следом за командой CALL, которая
вызвала данную процедуру, как показано ниже

Ранее, мы создали простую процедуру SumOf. Которая вычисляет сумму трех чисел,
находящихся в регистрах ЕАХ, ЕВХ и ЕСХ. Перед вызовом этой процедуры из процедуры main
нам нужно загрузить соответствующие значения в регистры ЕАХ, ЕВХ и ЕСХ:
INCLUDE Irvine32.inc
.data
theSum DWORD ?

.code
main PROC
mov eax,10000h ; Первый аргумент
mov ebx,20000h ; Второй аргумент
mov есх,30000h ; Третий аргумент
Arhitectura calculatoarelor (lucrări de laborator ) 60
call SumOf ; ЕАХ = ( ЕАХ + EBX + ECX)
mov theSum,eax ; Сохраним сумму в переменной
exit
main ENDP
SumOf PROC
add еах,еbх
add еах,есх
ret
SumOf ENDP
END main

Макропроцедурой (macro procedure) называется блок команд языка ассемблера. После


того как макропроцедура определена в программе, ее можно многократно вызывать в разных
участках кода. При вызове макропроцедуры, в код программы будут помещены содержащиеся в
ней команды. Не следует путать вызов макропроцедуры с вызовом обычной процедуры,
поскольку в первом случае команда CALL не используется.
Размещение. Определения макрокоманд, или макроопределения, помещаются либо
непосредственно в текст исходной программы на ассемблере (как правило, в его начало), либо в
отдельный текстовый файл, который включается в исходную программу на этапе компиляции с
помощью директивы INCLUDE. Текст макроопределения должен быть обработан ассемблером
до вызова макрокоманды в коде программы. Этим занимается препроцессор ассемблера. Он
выполняет анализ макроопределений и помещает их буфер. Как только в тексте программы
встречается имя макрокоманды, оно заменяется препроцессором на соответствующий набор
команд, который указан в ее макроопределении.
Синтаксис макроопределения следующий:
имя МАСRО Параметр-1, Параметр-2 ...

Список-команд

ENDM

Команды, находящиеся между директивами МАСRО и ENDM, до вызова макрокоманд не


компилируются. Макроопределение может содержать произвольное количество параметров,
которые разделяются запятыми.
В макроопределении можно указать, что некоторые параметры макрокоманды являются
обязательными. Для этого используется описатель REQ. Если при вызове макрокоманды
обязательный параметр будет опущен, компилятор сгенерирует сообщение об ошибке.
Например:
mPutchar МАСRО char:REQ
push еах
mov al,char call WriteChar рор еах
ENDM

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


нужно использовать описатель REQ.
Строка комментария в макроопределении начинается после двух символов точки с
запятой, стоящих подряд (;;). Данный тип комментария располагается только в
макроопределении и не переносится в исходный код, генерируемый при вызове макрокоманды.
Для вызова макрокоманды нужно просто поместить ее имя в исходный код
программы и при необходимости указать передаваемые ей значения:

Имя_макрокоманды Значение-1, Значение-2, …

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


следования в макроопределении, однако число значений необязательно должно соответствовать
числу параметров макрокоманды. Если в макрокоманду передается слишком много значений
параметров, компилятор выдаст соответствующее предупредительное сообщение. А если
Arhitectura calculatoarelor (lucrări de laborator ) 61
значений будет меньше, чем параметров макрокоманды, вместо недостающих параметров
подставляется пустая строка.

Пример: Сумирование 3 слов и сохранение результата в ax.

INCLUDE Irvine32.inc
addup MACRO ad1,ad2,ad3
mov ax,ad1
add ax,ad2
add ax,ad3
ENDM
.data
a DW 1
b DW 2
cd DW 3
d DW ?

.code
main PROC
addup a,b,c
mov dx,ax
addup dx,dx,dx
mov d,ax
addup d,dx,c
exit
main ENDP
END main

Локальные переменные объявляются в макроопределении директивой LOCAL (перед


любыми командами). В макроопределении может быть несколько директив LOCAL.

LOCAL имя {,имя} ...

Директива LOCAL, если присутствует в макроопределении, должна следовать за директивой


MACRO.
Пример: Возведение числа в степень.

power MACRO factor,exponent


LOCAL again, gotzero
xor dx,dx
mov ax,factor
mov cx,exponent
again: jcxz gotzero
mul bx
loop again
gotzero:
ENDM

4.3 Команды линейного и циклического сдвига


К командам линейного сдвига относятся команды, осуществляющие сдвиг по следующему
алгоритму:
 очередной “выдвигаемый” бит устанавливает флаг cf;
 бит, вводимый в операнд с другого конца, имеет значение 0;
 при сдвиге очередного бита он переходит во флаг cf, при этом значение предыдущего
сдвинутого бита теряется!
К командам линейного сдвига относятся следующие:
Arhitectura calculatoarelor (lucrări de laborator ) 62
Команда shl операнд,счетчик_сдвигов (Shift Logical Left) - логический сдвиг влево.
Содержимое операнда сдвигается влево на количество битов, определяемое значением
счетчик_сдвигов. Счетчик_сдвигов может быть и регистр CL (5 младших разрядов). Справа (в
позицию младшего бита) вписываются нули.

Ниже приведены допустимы форматы операндов команды SHL (тоже для SHR, SAL, SAR,
ROR, ROL, RCR, RCL):
SHL reg,imm8
SHL mem,imm8
SHL reg,CL
SHL mem,CL
Ex.:
mov bl,8Fh ; BL = 10001111b
shl bl,1 ; CF = 1, BL = 00011110b

mov al,10000000b
shl al,2 ; CF = 0, AL = 00000000b

mov dl,10 ; before: 00001010
shl dl,2 ; after: 00101000

Команда shr операнд,счетчик_сдвигов (Shift Logical Right) — логический сдвиг вправо.


Содержимое операнда сдвигается вправо на количество битов, определяемое значением
счетчик_сдвигов. Слева (в позицию старшего, знакового бита) вписываются нули.

Ex.:
mov al,0D0h ; AL = 11010000b
shr al,1 ; AL = 01101000b, CF = 0

mov al,00000010b
shr al,2 ; AL = 00000000b, CF = 1

Команда sal операнд,счетчик_сдвигов (Shift Arithmetic Left) — арифметический сдвиг


влево.
Содержимое операнда сдвигается влево на количество битов, определяемое значением
счетчик_сдвигов. Справа (в позицию младшего бита) вписываются нули. Команда sal не
сохраняет знака, но устанавливает флаг cf в случае смены знака очередным выдвигаемым
битом. В остальном команда sal полностью аналогична команде shl.

sar операнд,счетчик_сдвигов (Shift Arithmetic Right) — арифметический сдвиг вправо.


Содержимое операнда сдвигается вправо на количество битов, определяемое значением
счетчик_сдвигов. Слева в операнд вписываются нули. Команда sar сохраняет знак,
восстанавливая его после сдвига каждого очередного бита.
Arhitectura calculatoarelor (lucrări de laborator ) 63

Ex.:
mov al,0F0h ; AL = 11110000b (­16)
sar al,1 ; AL = 11111000b (­8), CF = 0

mov dl,­128 ; DL = 10000000b
sar dl,3 ; DL = 11110000b

mov ax,­128 ; EAX = ????FF80h
shl eax,16 ; EAX = FF800000h
sar eax,16 ; EAX = FFFFFF80h

Команды циклического сдвига


К командам циклического сдвига относятся команды, сохраняющие значения сдвигаемых бит.
rol операнд,счетчик_сдвигов (Rotate Left) — циклический сдвиг влево.
Содержимое операнда сдвигается влево на количество бит, определяемое операндом
счетчик_сдвигов. Сдвигаемые влево биты записываются в тот же операнд справа.

mov al,40h ; AL = 01000000b
rol al,1 ; AL = 10000000b, CF = 0
rol al,1 ; AL = 00000001b, CF = 1
rol al,1 ; AL = 00000010b, CF = 0

ror операнд,счетчик_сдвигов (Rotate Right) — циклический сдвиг вправо.


Содержимое операнда сдвигается вправо на количество бит, определяемое операндом
счетчик_сдвигов (0-255 или содержимое регистра CL). Сдвигаемые вправо биты записываются
в тот же операнд слева.

mov al,01h ; AL = 00000001b
ror al,1 ; AL = 10000000b, CF = 1
ror al,1 ; AL = 01000000b, CF = 0

mov al,00000100b
ror al,3 ; AL = 10000000b, CF = 1

Команды циклического сдвига в процессе своей работы осуществляют следующее:


циклически сдвигаемый бит не только вдвигается в операнд с другого конца, но и одновременно
его значение становиться значением флага cf.

Команды циклического сдвига через флаг переноса cf отличаются от команд простого


циклического сдвига тем, что сдвигаемый бит не сразу попадает в операнд с другого его конца,
а записывается сначала в флаг переноса cf. Лишь следующее исполнение данной команды сдвига
(при условии, что она выполняется в цикле) приводит к помещению выдвинутого ранее бита с
другого конца операнда.

К командам циклического сдвига через флаг переноса cf относятся следующие:


Arhitectura calculatoarelor (lucrări de laborator ) 64
rcl операнд,счетчик_сдвигов (Rotate through Carry Left) — циклический сдвиг влево через
перенос. Содержимое операнда сдвигается влево на количество бит, определяемое операндом
счетчик_сдвигов (0-255 или содержимое регистра CL). Сдвигаемые биты поочередно
становятся значением флага переноса cf.

clc ; CF = 0
mov bl,88h ; CF,BL = 0 10001000b
rcl bl,1 ; CF,BL = 1 00010000b
rcl bl,1 ; CF,BL = 0 00100001b

rcr операнд,счетчик_сдвигов (Rotate through Carry Right) — циклический сдвиг вправо


через перенос. Содержимое операнда сдвигается вправо на количество бит, определяемое
операндом счетчик_сдвигов(0-255 или содержимое регистра CL). Сдвигаемые биты поочередно
становятся значением флага переноса cf.

stc ; CF = 1
mov ah,10h ; AH, CF = 00010000 1
rcr ah,1 ; AH, CF = 10001000 0

Команды SHLD и SHRD


Команды SHLD и SHRD имеют не два, а три операнда. Команда SHLD (SHift Left DouЫe,
или сдвиг влево удвоенный) выполняет логический сдвиг влево операнда получателя данных на
количество разрядов, указанных в третьем операнде. Освободившиеся в результате сдвига
разряды операнда получателя данных заполняются старшими битами исходного (т.е. второго)
операнда. При этом значение исходного операнда не изменяется, но меняется состояние флагов
знака SF, нуля ZF, служебного переноса AF, четности PF и переноса CF. Синтаксис команды
SHLD следующий:
SHLD получатель, источник, счетчик

Сдвиг выполняется по следующей схеме:

Команда SHRD (SHift Right DouЫe, или сдвиг вправо удвоенный) выполняет логический
сдвиг вправо операнда получателя данных на количество разрядов, указанных в третьем
операнде. Освободившиеся в результате сдвига разряды операнда получателя данных
заполняются младшими битами исходного (т.е. второго) операнда. Синтаксис команды SHLD
следующий:

SHRD получатель, источник, счетчик


Arhitectura calculatoarelor (lucrări de laborator ) 65

Сдвиг выполняется по следующей схеме:

Команды SHLD и SHRD имеют одинаковый формат операндов, описанный ниже. Операнд-
получатель данных может располагаться либо в памяти, либо в регистре. Исходный операнд
может находиться только в регистре. В качестве счетчика может быть задан либо регистр CL,
либо 8-разрядная константа:

SHLD reg16,reg16,CL/imm8
SHLD mem16,reg16,CL/imm8
SHLD reg32,reg32,CL/imm8
SHLD mem32,reg32,CL/imm8

Ex.:
.data
wval WORD 9BA6h
.code
mov ax,0AC36h
shld wval,ax,4 ; wval = BA6Ah

mov ax,234Bh
mov dx,7654h
shrd ax,dx,4

4.4 Логические команды


В системе команд микропроцессора есть следующий набор команд, поддерживающих работу
с логическими данными:
and операнд_1,операнд_2 — операция логического умножения.
Команда выполняет поразрядно логическую операцию И (конъюнкцию) над битами
операндов операнд_1 и операнд_2. Результат записывается на место операнд_1.
Формат операндов:
AND reg,reg
AND reg,mem
AND reg,imm
AND mem,reg
AND mem,imm

Ех.
Arhitectura calculatoarelor (lucrări de laborator ) 66
mov al,10101110b
and al,11110110b ; result in AL = 10100110

or операнд_1,операнд_2 — операция логического сложения.


Команда выполняет поразрядно логическую операцию ИЛИ (дизъюнкцию) над битами
операндов операнд_1 и операнд_2. Результат записывается на место операнд_1.
mov al,11100011b
or al,00000100b ; result in AL = 11100111

xor операнд_1,операнд_2 — операция логического исключающего сложения.


Команда выполняет поразрядно логическую операцию исключающего ИЛИ над битами
операндов операнд_1 и операнд_2. Результат записывается на место операнд_1.

Таблица истинности:

test операнд_1,операнд_2 — операция “проверить” (способом логического умножения).


Команда выполняет поразрядно логическую операцию И над битами операндов операнд_1 и
операнд_2. Состояние операндов остается прежним, изменяются только флаги zf, sf, и pf, что
дает возможность анализировать состояние отдельных битов операнда без изменения их
состояния.

test al,00100000b; test bit 5 

not операнд — операция логического отрицания.


Команда выполняет поразрядное инвертирование (замену значения на обратное) каждого бита
операнда. Результат записывается на место операнда.
mov al,11110000b
not al ; AL = 00001111b

Для представления роли логических команд в системе команд микропроцессора очень важно
понять области их применения и типовые приемы их использования при программировании.
С помощью логических команд возможно выделение отдельных битов в операнде с целью их
установки, сброса, инвертирования или просто проверки на определенное значение.
Для организации подобной работы с битами операнд_2 обычно играет роль маски. С помощью
установленных в 1 битов этой маски и определяются нужные для конкретной операции биты
операнд_1. Покажем, какие логические команды могут применяться для этой цели:
Для установки определенных разрядов (бит) в 1 применяется команда :

or операнд_1,операнд_2

В этой команде операнд_2, выполняющий роль маски, должен содержать единичные биты на
месте тех разрядов, которые должны быть установлены в 1 в операнд_1.
or ax,10b ; установить 1-й бит в регистре ax

Для сброса определенных разрядов (бит) в 0 применяется команда:


Arhitectura calculatoarelor (lucrări de laborator ) 67
and операнд_1,операнд_2

В этой команде операнд_2, выполняющий роль маски, должен содержать нулевые биты на
месте тех разрядов, которые должны быть установлены в 0 в операнд_1.
and ax,0fffdh ;сбросить в 0 1-й бит в регистре eax
Команда xor операнд_1,операнд_2 применяется:
 для выяснения того, какие биты в операнд_1 и операнд_2 различаются;
 для инвертирования состояния заданных бит в операнд_1.
xor ax,10b ; инвертировать 1-й бит в регистре ax
jz mes ; переход, если 1-й бит в al был единичным
Интересующие нас биты маски (операнд_2) при выполнении команды xor должны быть
единичными, остальные — нулевыми.
Для проверки состояния заданных бит применяется команда:

test операнд_1,операнд_2 (проверить операнд_1)

Проверяемые биты операнд_1 в маске (операнд_2) должны иметь единичное значение.


Алгоритм работы команды test подобен алгоритму команды and, но он не меняет значения
операнд_1.
Результатом команды является установка значения флага нуля zf:
 если zf = 0, то в результате логического умножения получился нулевой результат, то есть
один единичный бит маски, который не совпал с соответствующим единичным битом
операнд_1;
 если zf = 1, то в результате логического умножения получился ненулевой результат, то
есть хотя бы один единичный бит маски совпал с соответствующим единичным битом
операнд_1.

test ax,0010h
jz m1 ; переход, если 4-й бит равен 1
Как видно из примера, для реакции на результат команды test целесообразно использовать
команду перехода jnz метка (Jump if Not Zero) — переход, если флаг нуля zf ненулевой, или
команду с обратным действием — jz метка (Jump if Zero) — переход, если флаг нуля zf = 0.

4.5 Команды для работы с отдельными битами


Команды BT, BTC, BTR и BTS предназначенны для работы с отдельными битами.

Команда ВТ
Команда ВТ (Bit Test, или тестирование бита) копирует бит первого операнда, номер n которого
указан во втором операнде, во флаг переноса CF:

ВТ строка_битов, n
Формат операндов:
BT r/m16,imm8
BT r/m16,r16
BT r/m32,imm8
BT r/m32,r32

Команда ВТС (Bit Test and Complement, или тестирование бита с инверсией) копирует бит
первого операнда, номер n которого указан во втором операнде, во флаг переноса CF, а затем
инвертирует значение этого бита.

Команда BTR (Bit Test and Reset, или тестирование бита со сбросом) копирует бит первого
операнда, номер n которого указан во втором операнде, во флаг переноса CF, а затем сбрасывает
(т.е. обнуляет) значение этого бита.
Arhitectura calculatoarelor (lucrări de laborator ) 68
Команда BTS (Bit Test апd Set, или тестирование бита с установкой) копирует бит первого
операнда, номер n которого указан во втором операнде, во флаг переноса CF, а затем устанавливает
в единицу значение этого бита.
В приведенном ниже примере во флаг переноса CF помещается значение 6-ro бита переменной
perem, а затем значение этого бита устанавливается в единицу:

Ex.
.data
perem WORD 10001000b

.code
bts perem,6 ; CF=0 perem=11001000b

Пример. Следующая программа выполняет поиск первого пробела в последовательности


символов str. По завершении программы, если нет пробелов в ax – 0, иначе значение порядкового
номера первого пробела в последовательности. Обозначим long - количество символов в
последовательности.

.data
str DB 'Poisc pervogo probela!'
long EQU sizeof str

.code
mov ecx,long
mov esi,-1
mov al,' '
next: inc esi
cmp al,str[esi]
loopne next
jne nol
mov eax,esi
jmp exit
nol: mov eax,0
exit: nop

4.6 Рассмотрим несколько примеров применения вышеприведенных команд


Пример 1. Следующая программа выполняет последовательный поиск первого пробела в
ASCII последовательности pos. Если пробел не найден в EAX записывается 0, а если
найден запишется номер позиции пробела в последовательности. Последовательность
pos имеет l символов.
.data
pos DB ' This is a sequence!'
l EQU sizeof pos

.code
main proc

mov ecx,l
mov esi,-1
mov al,' '
sld: inc esi
cmp al,pos[esi]
loopne sld
jne ne_naiden
mov eax,l
sub eax,ecx
Arhitectura calculatoarelor (lucrări de laborator ) 69
jmp vihod
ne_naiden: mov eax,0
vihod: nop
exit
main ENDP
END main

Пример 2. Следующая программа выполняет сложение двух чисел в упакованном BCD


формате. Размерность чисел n байт.
.DATA
bcd1 DB 22h,41h,75h,32h; 32754122
bcd2 DB 31h,27h,53h,62h; 62532731
n EQU lengthof bcd2
.CODE

lea si,bcd1
lea di,bcd2
mov cx,n
clc
cycle: mov al,[si]
adc al,[di]
daa
mov [di],al
inc si
inc di
loop cycle
exit

Пример 3. Следующая программа выполняет сложение двух чисел представленных в виде


двух последовательностей ASCII символов.
.DATA
asc1 DB '7','5',2','1','3'; 31257
asc2 DB '6','1',9','9','5'; 59916
l EQU sizeof asc1
.CODE

lea esi,asc1
lea edi,asc2
mov ecx,l
clc
cycle: mov al,[esi]
adc al,[edi]
aaa
mov [edi],al
inc esi
inc edi
loop cycle
exit

Пример 4. Следующая программа выполняет умножение некоторого числа определенного


как последовательность десятичных цифр в коде ASCII на число ASCII. Результат
сохранится в неупакованном BCD формате.

.CODE
asc1 DB '2','4','7','1','3' ; 31742
Arhitectura calculatoarelor (lucrări de laborator ) 70
l EQU sizeof asc1
dgt DB '4'
asc2 DB 6 DUP(?)
.CODE

lea di,asc2
lea si,asc1
mov cx,l
mov dl,dgt
mov BYTE PTR [di],0
and al,0fh
cycle: mov al,[si]
inc si
and al,0fh
mul dl
aam
add al,[di]
aaa
mov [di],al
inc di
mov [di],ah
loop cycle
exit

Пример 5. Разработать программу, которая выполняет деление некоторого целого числа,


определенного как последовательность десятичных цифр в коде ASCII на десятичную цифру в
коде ASCII. Результат сохранится в неупакованном BCD формате.

.DATA
asc1 DB '7','2','1','8','9'; 72189
dgt DB '6'; 6
asc2 DB 5 DUP(?)
l EQU sizeof asc1
.CODE

lea si,asc1
lea di,asc2
mov cx,l
mov dl,dgt
and dl,0Fh
xor ah,ah
cicl: mov al,[si]
inc si
and al,0Fh
aad
div dl
mov [di],al
inc di
loop cicl
exit

Пример 6. Программа (16-битная программа, использует функции MSDOS) выполняет:


Считывается символ со стандартного ввода и в зависимости от его значения выполняются
следующие действия: если этот символ - 'a', на экран выводится последовательность 'Alfa', если 'g',
выводится - 'Gama', если 'd' - 'Delta', если другое значение, то выводится последовательность
'***':
Arhitectura calculatoarelor (lucrări de laborator ) 71
INCLUDE Irvine16.inc
.DATA
cars DB 'a','g','d'
pos_a DB 'Alfa',13,10,'$'
pos_g DB 'Gama',13,10,'$'
pos_d DB 'Delta',13,10,'$'
pos DB '***',13,10,'$'
act_tbl DW act_a,act_g,act_d
.CODE
main PROC
mov ax,@data
mov ds,ax

mov ah,01h
int 21h
xor bx,bx
next: cmp bx,02h
ja act
cmp al,cars[bx]
je found
inc bx
jmp SHORT next
found: shl bx,1
jmp act_tbl[bx]
act_a: mov dx,OFFSET pos_a
mov ah,09h
int 21h
jmp SHORT vihod
act_g: mov dx,OFFSET pos_g
mov ah,09h
int 21h
jmp SHORT vihod
act_d: mov dx,OFFSET pos_d
mov ah,09h
int 21h
jmp SHORT vihod
act: mov dx,OFFSET pos
mov ah,09h
int 21h
vihod: mov ah,01
int 21h
exit
main ENDP
END main

Используя macro, программа может быть переписана следующим способом:

INCLUDE Irvine16.inc
.DATA
cars DB 'a','g','d'
pos_a DB 'Alfa',13,10,'$'
pos_g DB 'Gama',13,10,'$'
pos_d DB 'Delta',13,10,'$'
pos DB '***',13,10,'$'
act_tbl DW act_a,act_g,act_d
Arhitectura calculatoarelor (lucrări de laborator ) 72

afis macro x,y


mov dx,OFFSET x
mov ah,09h
int 21h
jmp SHORT y
endm

.CODE
main PROC
mov ax,@data
mov ds,ax

mov ah,01h
int 21h
xor bx,bx
next: cmp bx,02h
ja act
cmp al,cars[bx]
je found
inc bx
jmp SHORT next
found: shl bx,1
jmp act_tbl[bx]
act_a: afis pos_a,vihod
act_g: afis pos_g,vihod
act_d: afis pos_d,vihod
act: mov dx,OFFSET pos
mov ah,09h
int 21h

vihod: mov ah,01


int 21h
exit
main ENDP
END main

Пример 7. Предполагается, что байт по адресу perem содержит значения логических переменных
x7, x6, x5, x4, x3, x2, x1, x0. Разработать программу, выполняющую следующую логическую функцию:
f  x 7 , x 6 ,..., x 0   x 7 x 5 x1 x 0  x 3 x 2 x 0  x 6 x 4 x1
Результат сохранить в ah.

.DATA
perem DB 10010011b
.CODE

mov al,perem
mov bl,01011100b
or bl,al
mov bh,11110010b
or bh,al
mov cl,10101101b
or cl,al
xor bl,11011111b
jz true
Arhitectura calculatoarelor (lucrări de laborator ) 73
xor bh,11111110b
jz true
xor cl,11101101b
jz true
xor ah,ah
jmp SHORT continue
true: mov ah,1
continue: nop
exit

Пример 8. Разработать программу, которая переведет некоторое 16-разрядное десятичное число в


неупакованном BCD формате в упакованный формат.

.DATA
unpck DB 1,2,4,0,7,8,9,0,6,7,9,1,2,5,8,3
pck DB 8 DUP(?)
.CODE

mov dx,8
mov cl,4
xor si,si
mov di,si
conv: mov ax,WORD PTR unpck[si]
shl al,cl
shr ax,cl
mov pck[di],al
add si,2
inc di
dec dx
jnz conv
exit

Пример 9. {Заполнение экрана последовательностью символов введенных с клавиатуры}


Следующая программа читает последовательность из максимум 16-ти символов введенных с
клавиатуры и выводит 24 строки по 80 символов в строке содержащие введенную
последовательность:

.DATA
buffer LABEL BYTE
max_length DB ? ; максимальная длина
chars_entered DB ? ; количество введенных символов
string DB 17 DUP(?) ; последовательность из 17 символов,
; считается и CR
strings_per_line DW 0 ; сколько последовательностей
;поместятся в одну строку
crlf DB 0Dh,0Ah,'$'

.CODE

mov dx,OFFSET buffer


mov buffer,17
mov ah,0Ah
int 21h
xor bx,bx
Arhitectura calculatoarelor (lucrări de laborator ) 74
mov bl,chars_entered ; в bl – длина последовательности
mov buffer[bx+2],'$'
mov al,80
cbw
div chars_entered ; сколько последовательностей
;поместятся в одну строку
xor ah,ah
mov strings_per_line,ax
mov cx,25
display_screen: push cx
mov cx,strings_per_line
display_line: mov dx,OFFSET string ; вывод последовательности
mov ah,09h
int 21h
loop display_line
mov dx,OFFSET crlf
mov ah,09h
int 21h
pop cx
loop display_screen
exit

Пример 10. Следующая программа (64 бит) суммирует два шестнадцатеричных числа и выводит
результат на экран.
ExitProcess proto
WriteString proto
WriteHex64 proto
Crlf proto

.data
qp1 qword 1234h
qp2 qword 2345h
var1 byte "X=",0
var2 byte "Y=",0
var3 byte " X+Y=",0

.code
main proc

mov rdx,offset var1


call WriteString
mov rax,qp1
call WriteHex64
call Crlf

mov rdx,offset var2


call WriteString
mov rax,qp2
call WriteHex64
call Crlf

mov rdx,offset var3


call WriteString
mov rax,qp1
add rax,qp2
call WriteHex64
call Crlf
Arhitectura calculatoarelor (lucrări de laborator ) 75
mov ecx,0
call ExitProcess

main endp
end

4.7 Задание к лабораторной работе (применяйте MACRO)


Необходимо выполнить ассемблирование и анализ в пошаговом режиме в Debug (п. 4.6)

4.8 Варианты индивидуальных заданий


Данные вводить с клавиатуры, а результаты вывести на экран.

1.Определить сколько единичных бит и в каком разряде находится первая единица слева и
соответственно справа в слове по адресу slovo и в 32-битном слове начинающегося по адресу
dslovo. Пример вывода на экран:
slovo 0101010101010000
dslovo 00001111000011110000111100001111
quantity 1: 6, 16 ; количество 1 в slovo и dslovo
left: 14, 27 ; позиция первых 1-х бит слева, в slovo (14) и dslovo (27)
right: 4, 0 ; соответственно справа

2. С клавиатуры вводится последовательность до 25 символов, завершение ввода Enter и


записываются в памяти с адреса string. Необходимо разработать программу, выполняющую
упомянутое чтение и устанавливает в “1” 5-й бит регистра dl, если введенный string содержит хотя
бы одну шестнадцатеричную цифру, иначе этот бит установится в «0». Содержимое регистра
вывести на экран в двоичном коде.

3. Напишите программу, которая переводит некоторое число из 16-ти цифр в упакованном BCD
формате расположенного по адресу packed в число в неупакованном BCD формате по адресу
unpacked. Последовательности вывести на экран в двоичном коде.

4. Разработать программу, которая переводит некоторую последовательность в обратную


последовательность. Последовательность состоит из известного количества символов и находится
по адресу string. Эту же задачу решить для случая, когда неизвестно количество символов в
строке, но последним символом в строке является @.

5. Разработайте программу, которая группирует 4 величины по 12 бит записанных в 4


последовательных слова в памяти, в 3 последовательных слова в памяти. К примеру,
4 слова – 0123h, 0456h, 0789h, 0abch в
3 слова- 1234h, 5678h, 9abch.

6. Разработайте программу, которая читает последовательность из 4-х шестнадцатеричных цифр


(числа BCD), введенных с клавиатуры, переводит это число в неупакованный BCD формат.
Вывести оба числа на экран. К примеру, 8976 в- 08090706.

7. Напишите программу, которая умножит два шестнадцатеричных числа, разрядностью 64 бита


dp1 и dp2 (по примеру 10).

8. С клавиатуры вводится последовательность из 10-ти символов, напишите программу, которая


выводит на экран 20 строк по 75 символов в строке, содержащие введенную последовательность.

Содержание отчета. Отчёт по лабораторной работе должен содержать программы 3-x


примеров (*.asm) п. 4.6, а также разработанные программы индивидуального задания из п.
4.8.
Arhitectura calculatoarelor (lucrări de laborator ) 76
4.9 Задачи для неаудиторной работы
Вопросы для подготовки к лабораторной работе 5: Обьясните команды MOVSB, MOVSW,
MOVSD, CMPSB, CMPSW, CMPSD, SCASB, SCASW, SCASD, STOSB, STOSW, STOSD,
LODSB, LODSW, LODSD.

ЛАБОРАТОРНАЯ РАБОТА №5
СОЗДАНИЕ И ОБРАБОТКА МАССИВОВ ДАННЫХ

5.1. Цель работы:


Работа рассматривает создание и обработку массивов данных.

5.2. Цепочечные команды

Эти команды также называют командами обработки строк символов. Под строкой символов
здесь понимается последовательность байт, а цепочка — это более общее название для случаев,
когда элементы последовательности имеют размер больше байта — слово или двойное слово.
Особенность всех цепочечных команд в том, что они, кроме обработки текущего элемента
цепочки, осуществляют еще и автоматическое продвижение к следующему элементу данной
цепочки.

Команды MOVSB (Move (copy) bytes)


MOVSW (Move (copy) words)
MOVSD (Move (copy) doublewords)

Эти команды позволяют скопировать данные из одного участка памяти, адрес которого указан в
регистре ESI, в другой участок памяти, адрес которого указан в регистре EDI. При этом, в
зависимости от состояния флага направления, значение в регистрах ESI и EDI либо
увеличивается, либо уменьшается.
Назначение: пересылка элементов двух последовательностей (цепочек) в памяти.
Алгоритм работы:
выполнить копирование байта, слова или двойного слова из операнда источника в операнд
приемник, при этом адреса элементов предварительно должны быть загружены:
 адрес источника — в esi
 адрес приемника — в edi
в зависимости от состояния флага df изменить значение регистров esi и edi:
 если df=0, то увеличить содержимое этих регистров на длину структурного элемента
последовательности;
 если df=1, то уменьшить содержимое этих регистров на длину структурного элемента
последовательности;
если есть префикс повторения, то выполнить определяемые им действия.
Состояние флагов после выполнения команды: выполнение команды не влияет на флаги
Применение: Команды пересылают элемент из одной ячейки памяти в другую. Размеры
пересылаемых элементов зависят от применяемой команды.
С командами МОVSВ, МOVSW и МOVSD может использоваться префикс повторения.
Существует несколько типов префиксов повторения:
Arhitectura calculatoarelor (lucrări de laborator ) 77
REP - Повторять команду, пока ЕСХ > О
REPZ,REPE - Повторять команду, пока ЕСХ > О и флаг нуля установлен (ZF =1)
REPNZ,REPNE - Повторять команду, пока ЕСХ > О и флаг нуля сброшен (ZF = 0)

Ex. Необходимо скопировать 20 двойных слов из последовательности source в


последовательность target:
.data
source DWORD 20 DUP(0FFFFFFFFh)
target DWORD 20 DUP(?)
.code
cld  ; Сбросим флаг DF и установим 
;прямое направление
mov ecx,LENGTHOF source ; Зададим значение счетчика REP 
mov esi,OFFSET source ; Зададим адрес источника данных
mov edi,OFFSET target ; Зададим адрес получателя данных
rep movsd  ; Копируем 20 двойных слов

Команды CMPSB (Compare bytes)


CMPSW (Compare words)
CMPSD (Compare doublewords)

Эти команды позволяют сравнить данные из одного участка памяти, адрес которого
указан в регистре ESI, с другим участком памяти, адрес которого указан в регистре EDI. Типы
команд CMPS приведены ниже.
CMPSB - Сравнивает последовательность байтов
CMPSW - Сравнивает последовательность слов
CMPSD - Сравнивает последовательность двойных слов.

.data
source DWORD 1234h
target DWORD 5678h
.code
mov esi,OFFSET source
mov edi,OFFSET target
cmpsd  ; compare doublewords
ja L1  ; jump if source > target
Ех2.:
mov esi,OFFSET source
mov edi,OFFSET target
cld ; direction = forward
mov ecx,LENGTHOF source ; repetition counter
repe cmpsd ; repeat while equal

Команды SCASB (SCAS- Scans a string)


SCASW
SCASD

Эти команды сравнивают значение, находящееся в регистрах AL/AX/EAX с байтом,


словом или двойным словом, адресуемым через регистр ЕDI.
Данная группа команд обычно используется при поиске какого-либо значения в длинной
строке или массиве. Если перед командой SCAS поместить префикс REPE (или REP), строка
или массив будет сканироваться до тех пор, пока значение в регистре ЕСХ не станет равным
нулю, либо пока не будет найдено значение в строке или массиве, отличное от того, что
находится в регистре AL/ АХ/ЕАХ (т.е. пока не будет сброшен флаг нуля ZF). При
использовании префикса REPNE, строка или массив будет сканироваться до тех пор, пока
Arhitectura calculatoarelor (lucrări de laborator ) 78
значение в регистре ЕСХ не станет равным нулю, либо пока не будет найдено значение в строке
или массиве, совпадающее с тем, что находится в регистре AL/AX/EAX (т.е. пока не будет
установлен флаг нуля ZF).
Поиск символов в строке. В приведенном ниже фрагменте кода выполняется поиск символа F в
строке alpha. При нахождении данного символа, в регистре EDI будет содержаться его адрес
плюс единица. Если же искомого символа нет в исходной строке, то работа программы
завершается в результате перехода по команде JNZ:
.data
alpha BYTE "ABCDEFGH",0
.code
mov edi,OFFSET alpha ; Загрузим в EDI адрес строки alphа
mov al,'F' 
mov ecx,LENGTHOF alpha ; загрузим в ЕСХ длину строки alphа
cld  ; direction = forward
repne scasb ; Сканируем строку пока не найдем символ "F"
jnz quit ; Если не нашли, завершим работ

В этом примере после команды REPNE SCASB находится команда условного перехода JNZ,
которая срабатывает в случае, когда символ "F" в исходной строке найден не будет (т.е. когда
работа команды REPNE SCASB завершится по условию ЕСХ = 0, а не ZF = 1).

Команды STOSB (STOS- Store string data)


STOSW
STOSD

Эта группа команд позволяет сохранить содержимое регистра AL/AX/EAX в памяти,


адресуемой через регистр EDI. При выполнении команды sтos содержимое регистра EDI
изменяется в соответствии со значением флага направления DF и типом используемого в
команде операнда. При использовании совместно с префиксом REP, с помощью команды SТOS
можно записать одно и то же значение во все элементы массива или строки.

.data
Count = 100
string1 BYTE Count DUP(?)
.code
mov al,0FFh ; Записываемое значение
mov edi,OFFSET string1 ; Загрузим в EDI адрес строки
mov ecx,Count ; Загрузим в ЕСХ длину строки
cld ; Направление сравнения -восходящее
rep stosb ; Заполним строку содержимым AL

Команды LODSB (LODS- Load Accumulator from String)


LODSW
LODSD
Эта группа команд позволяет загрузить в регистр AL/ АХ/ЕАХ содержимое байта, слова
или двойного слова памяти, адресуемого через регистр ESI. При выполнении команды LODS
содержимое регистра ESI изменяется в соответствии со значением флага направления DF и
типом используемого в команде операнда. Префикс REP практически никогда не используется с
командой LODS, поскольку при этом будет теряться предыдущее значение, загруженное в
аккумулятор. Таким образом, эта команда используется для загрузки одного значения в
аккумулятор.
Arhitectura calculatoarelor (lucrări de laborator ) 79
ЕХ. В приведенной ниже программе каждый элемент массива двойных слов array
умножается на постоянное значение. Для загрузки в регистр ЕАХ текущего элемента массива
используется команда LODSD, а для сохранения - STOSD.

INCLUDE Irvine32.inc
.data
array DWORD 1,2,3,4,5,6,7,8,9,10 ; test data
multiplier DWORD 10 
.code
main PROC
cld  ; direction = forward
mov esi,OFFSET array ; Загрузим адрес массива 
mov edi,esi  ; в регистры ESI и EDI
mov ecx,LENGTHOF array ; Загрузим длину массива
L1: lodsd  ; Загрузим текущий элемент [ESI] в EAX
mul multiplier 
stosd ; Запишем ЕАХ в текущий элемент 
;массива (его адрес в [EDI]) 
loop L1
exit
main ENDP
END main

5.3 Массивы
Дадим формальное определение: массив - структурированный тип данных, состоящий из
некоторого числа элементов одного типа.
При необходимости использовать массив в программе его нужно моделировать одним из
следующих способов:
Перечислением элементов массива в поле операндов одной из директив описания данных. При
перечислении элементы разделяются запятыми. К примеру:

;массив из 5 элементов.Размер каждого элемента 4 байта:


mas dd 1,2,3,4,5
Используя оператор повторения dup. К примеру:
;массив из 5 нулевых элементов.
;Размер каждого элемента 2 байта:
mas dw 5 dup (0)

Такой способ определения используется для резервирования памяти с целью размещения и


инициализации элементов массива.
Используя директивы label и rept. Пара этих директив может облегчить описание больших
массивов в памяти и повысить наглядность такого описания. Директива rept относится к
макросредствам языка ассемблера и вызывает повторение указанное число раз строк,
заключенных между директивой и строкой endm. К примеру, определим массив байт в области
памяти, обозначенной идентификатором mas_b. В данном случае директива label определяет
символическое имя mas_b, аналогично тому, как это делают директивы резервирования и
инициализации памяти. Достоинство директивы label в том, что она не резервирует память, а
лишь определяет характеристики объекта. В данном случае объект — это ячейка памяти.
Используя несколько директив label, записанных одна за другой, можно присвоить одной и той
же области памяти разные имена и разный тип, что и сделано в следующем фрагменте:
...
n=0
...
Arhitectura calculatoarelor (lucrări de laborator ) 80
mas_b label byte
mas_w label word
rept 4
dw 0f1f0h
endm
В результате в памяти будет создана последовательность из четырех слов f1f0. Эту
последовательность можно трактовать как массив байт или слов в зависимости от того, какое
имя области мы будем использовать в программе — mas_b или mas_w.
Использование цикла для инициализации значениями области памяти, которую можно будет
впоследствии трактовать как массив. Посмотрим на примере 5.1, каким образом это делается.
Пример 5.1

INCLUDE Irvine32.inc
.data
mes db 0ah,0dh,'String - ',0
mas db 10 dup (?),0 ;исходный массив
i db 0
.code
main proc

xor eax,eax ;обнуление eax


mov ecx,10 ;значение счетчика цикла в ecx
mov esi,0;индекс начального элемента в esi
go: ;цикл инициализации
mov bh,i ;i в bh
mov mas[esi],bh ;запись в массив i
inc i ;инкремент i
inc esi ;продвижение к следующему элементу массива
loop go ;повторить цикл
;вывод на экран получившегося массива
mov edx,OFFSET mes
call WriteString

mov ecx,10
mov esi,0
show:
movsx eax,mas[esi]
call WriteInt
inc esi
loop show
quit:
call crlf
exit
main ENDP
END main

Доступ к элементам массива


При работе с массивами необходимо четко представлять себе, что все элементы массива
располагаются в памяти компьютера последовательно. Для того чтобы локализовать
определенный элемент массива, к его имени нужно добавить индекс. Так как мы моделируем
массив, то должны позаботиться и о моделировании индекса. В языке ассемблера индексы
массивов — это обычные адреса, но с ними работают особым образом. Другими словами, когда
при программировании на ассемблере мы говорим об индексе, то скорее подразумеваем под
этим не номер элемента в массиве, а некоторый адрес.
Arhitectura calculatoarelor (lucrări de laborator ) 81
Давайте еще раз обратимся к описанию массива. К примеру, в программе статически
определена последовательность данных:
mas dw 0,1,2,3,4,5
Пусть эта последовательность чисел трактуется как одномерный массив. Размерность каждого
элемента определяется директивой dw, то есть она равна 2 байта. Чтобы получить доступ к
третьему элементу, нужно к адресу массива прибавить 6. Нумерация элементов массива в
ассемблере начинается с нуля. То есть в нашем случае речь, фактически, идет о 4-м элементе
массива — 3, но об этом знает только программист; микропроцессору в данном случае все
равно — ему нужен только адрес.
В общем случае для получения адреса элемента в массиве необходимо начальный (базовый)
адрес массива сложить с произведением индекса (номер элемента минус единица) этого
элемента на размер элемента массива:
база + (индекс*размер элемента)
К примеру:
mas dw 0,1,2,3,4,5
...
mov si,4
;поместить 3-й элемент массива mas в регистр ax:
mov ax,mas[si]
Микропроцессор позволяет масштабировать индекс. Это означает, что если указать после
имени индексного регистра знак умножения “*” с последующей цифрой 2, 4 или 8, то
содержимое индексного регистра будет умножаться на 2, 4 или 8, то есть масштабироваться.
В качестве примера использования масштабирования рассмотрим пример 5.2, в котором
просматривается массив, состоящий из слов, и производится сравнение этих элементов с нулем.
Выводится соответствующее сообщение.
;Пример 5.2. Просмотр массива слов с использованием
;масштабирования

INCLUDE Irvine32.inc
.data;начало сегмента данных
;тексты сообщений:
mes1 db ' ne raven 0!',0ah,0dh,0
mes2 db ' raven 0!',0ah,0dh,0
mes3 db 0ah,0dh,'Element ',0
mas dw 2,7,0,0,1,9,3,6,0,8 ;исходный массив
.code

main proc

xor eax,eax ;обнуление ax


prepare:
mov ecx,10 ;значение счетчика цикла в ecx
mov esi,0;индекс в esi
compare:
mov dx,mas[esi*2] ;первый элемент массива в dx
cmp dx,0 ;сравнение dx c 0
je equal;переход, если равно
not_equal: ;не равно
mov edx,OFFSET mes3
call WriteString ;вывод сообщения на экран

mov eax,esi ;вывод номера элемента массива на экран


call WriteDec

mov edx,OFFSET mes1


Arhitectura calculatoarelor (lucrări de laborator ) 82
call WriteString

inc esi ;на следующий элемент


dec ecx ;условие для выхода из цикла
jecxzquit ;ecx=0? Если да — на выход
jmp compare ;нет — повторить цикл
equal: ;равно 0

mov edx,OFFSET mes3 ;вывод сообщения mes3 на


экран
call WriteString

mov eax,esi
call WriteDec

mov edx,OFFSET mes2 ;вывод сообщения mes2 на


экран
call WriteString

inc esi ;на следующий элемент


dec ecx ;все элементы обработаны?
jecxzquit
jmp compare
quit:
call crlf
exit
main ENDP
END main ;конец программы
Масштабирование эффективно лишь тогда, когда размерность элементов массива равна 2, 4 или
8 байт. Если же размерность элементов другая, то организовывать обращение к элементам
массива нужно обычным способом, как описано ранее.
Рассмотрим пример работы с массивом из пяти трехбайтовых элементов (пример 5.3). Младший
байт в каждом из этих элементов представляет собой некий счетчик, а старшие два байта —
что-то еще, для нас не имеющее никакого значения. Необходимо последовательно обработать
элементы данного массива, увеличив значения счетчиков на единицу.
Пример 5.3 Обработка массива элементов с нечетной длиной

INCLUDE Irvine32.inc
.data ;начало сегмента данных
N=5 ;количество элементов массива
mas byte 5 dup (3 dup (0))
.code ;сегмент кода
main proc ;точка входа в программу

xor eax,eax ;обнуление eax


mov esi,0;0 в esi
mov ecx,N;N в ecx
go:
mov dl,mas[esi] ;первый байт поля в dl
inc dl ;увеличение dl на 1 (по условию)
mov mas[esi],dl ;запись обратно в массив
add esi,3;сдвиг на следующий элемент массива
loop go ;повтор цикла
mov esi,0;подготовка к выводу на экран
mov ecx,N
Arhitectura calculatoarelor (lucrări de laborator ) 83
show: ;вывод на экран содержимого
;первых байт полей

movsx eax,mas[esi]
call WriteDec
add esi,3

loop show
quit:
call crlf
exit ; выход
main ENDP
END main ;конец программы
5.4 Двухмерные массивы
Двухмерный массив нужно моделировать. Память под массив выделяется с помощью
директив резервирования и инициализации памяти.
Непосредственно моделирование обработки массива производится в сегменте кода, где
программист, описывая алгоритм обработки ассемблеру, определяет, что некоторую область
памяти необходимо трактовать как двухмерный массив. Если последовательность однотипных
элементов в памяти трактуется как двухмерный массив, расположенный по строкам, то адрес
элемента (i, j) вычисляется по формуле
(база + количество_элементов_в_строке * размер_элемента * i+j)
Здесь i = 0...n–1 указывает номер строки, а j = 0...m–1 указывает номер столбца.
Например, пусть имеется массив чисел (размером в 1 байт) mas(i, j) с размерностью 4 на 4
(i= 0...3, j = 0...3):
23 04 05 67
05 06 07 99
67 08 09 23
87 09 00 08
В памяти элементы этого массива будут расположены в следующей последовательности:
23 04 05 67 05 06 07 99 67 08 09 23 87 09 00 08
Если мы хотим трактовать эту последовательность как двухмерный массив, приведенный выше,
и извлечь, например, элемент
mas(2, 3) = 23,
то проведя подсчет, убедимся в правильности наших рассуждений:
Эффективный адрес mas(2, 3) = mas + 4 * 1 * 2 + 3 = mas + 11
Посмотрите на представление массива в памяти и убедитесь, что по этому смещению
действительно находится нужный элемент массива.
Организовать адресацию двухмерного массива логично, используя рассмотренную нами
ранее базово-индексную адресацию. При этом возможны два основных варианта выбора
компонентов для формирования эффективного адреса:
– сочетание прямого адреса, как базового компонента адреса, и двух индексных регистров
для хранения индексов:
mov ax,mas[ebx][esi]
– сочетание двух индексных регистров, один из которых является и базовым и индексным
одновременно, а другой — только индексным:
mov ax,[ebx][esi]
В программе это будет выглядеть примерно так:
;Фрагмент программы выборки элемента
;массива mas(2,3) и его обнуления
.data
mas db 23,4,5,67,5,6,7,99,67,8,9,23,87,9,0,8
i=2
j=3
.code
Arhitectura calculatoarelor (lucrări de laborator ) 84
...
mov esi,4*1*i
mov edi,j
mov al,mas[esi][edi] ;в al элемент mas(2,3)
...
В качестве законченного примера рассмотрим программу поиска элемента в двухмерном
массиве чисел (пример 5.4). Элементы массива заданы статически.
Пример 5.4 - Поиск элемента в двухмерном массиве.

INCLUDE Irvine32.inc
.data
;матрица размером 2x5 — если ее не инициализировать,
;то для наглядности она может быть описана так:
;array dw 2 DUP (5 DUP (?))
;но мы ее инициализируем:
arraydw 1,2,3,4,5,6,7,3,9,0
;логически это будет выглядеть так:
;array= {1 2}
; {3 4}
; {5 6}
; {7 3}
; {9 0}
elem dw 3 ;элемент для поиска
failed db 0ah,0dh,'Net takogo elementa v masive!',0
success db 0ah,0dh,'Takoi element v masive prisutstvuet ',0
foundtime db ? ;количество найденных элементов
fnd db ' raz(a)',0ah,0dh,0
.code
main proc

xor eax,eax
mov esi,0;esi=столбцы в матрице
mov ebx,0;ebx=строки в матрице
mov ecx,5;число для внешнего цикла (по строкам)
external: ;внешний цикл по строкам
mov ax,array[ebx][esi];в ax первый элемент матрицы
push ecx ;сохранение в стеке счётчика внешнего цикла
mov ecx,2;число для внутреннего цикла (по столбцам)
mov esi,0
iternal: ;внутренний цикл по строкам
inc esi ;передвижение на следующий элемент в строке
;сравниваем содержимое текущего элемента в ax с искомым
элементом:
cmp ax,elem
;если текущий совпал с искомым, то переход на here для
обработки,
;иначе цикл продолжения поиска
Je here
;иначе — цикл по строке ecx=2 раз
loop iternal
here:
jecxzmove_next ;просмотрели строку?
inc foundtime ;иначе увеличиваем счётчик совпавших
move_next: ;продвижение в матрице
pop ecx ;восстанавливаем ECX из стека (5)
Arhitectura calculatoarelor (lucrări de laborator ) 85
add ebx,1 ;передвигаемся на следующую строку
loop external ;цикл (внешний)
cmp foundtime,0h ;сравнение числа совпавших с 0
ja eql ;если больше 0, то переход
not_equal: ;нет элементов, совпавших с искомым

mov edx,OFFSET failed


call WriteString ;вывод сообщения на экран

jmp quit ;на выход


eql: ;есть элементы, совпавшие с искомым
mov edx,OFFSET success
call WriteString ;вывод сообщения на экран

movsx eax,foundtime
call WriteDec
mov edx,OFFSET fnd
call WriteString

quit: ;выход
call crlf
exit ; выход
main ENDP
END main ;конец программы
При анализе работы программы не забывайте, что в языке ассемблера принято элементы
массива нумеровать с 0. При поиске определенного элемента массив просматривается от начала
и до конца. Приведенная программа сохраняет в поле foundtime количество вхождений искомого
элемента в массив. В качестве индексных регистров используются esi и ebx.

5.5 Типовые операции с массивами


Для демонстрации основных приемов работы с массивами лучше всего подходят программы
поиска или сортировки.
Рассмотрим одну такую программу, выполняющую сортировку массива по возрастанию
(пример 5.5).
Пример 5.5 - Сортировка массива

INCLUDE Irvine32.inc
.data
mes1db 0ah,0dh,'Ishodnii massiv',0ah,0dh,0
;некоторые сообщения
mes2db 0ah,0dh,'Otsortirovanii massiv',0ah,0dh,0
n equ 9 ;количество элементов в массиве, считая с 0
mas dw 2,7,4,0,1,9,3,6,5,8 ;исходный массив
tmp dw 0 ;переменные для работы с массивом
I dw 0
J dw 0
.code
main proc
xor eax,eax
;вывод на экран исходного массива
mov edx,OFFSET mes1 ;вывод сообщения mes1
call WriteString

mov ecx,10
mov esi,0
Arhitectura calculatoarelor (lucrări de laborator ) 86
show_primary: ;вывод значения элементов
;исходного массива на экран
movsx eax,mas[esi]
call WriteDec

add esi,2
loop show_primary

;строки с метки M1 до метки M2 программы эквивалентны ;следующему


коду на языке С:
;for (i=0;i<9;i++)
; for (j=9;j>i;j--)
; if (mas[i]>mas[j])
; {tmp=mas[i];
; mas[i]=mas[j];
; mas[j]=tmp;}
M1: mov i,0 ;инициализация i
;внутренний цикл по j
internal:
mov j,9 ;инициализация j
jmp cycl_j ;переход на тело цикла
exchange:
movsxebx,i ;bx=i
shl ebx,1
mov ax,mas[ebx] ;ax=mas[i]
movsxebx,j ;bx=j
shl ebx,1
cmp ax,mas[ebx] ;mas[i] ? mas[j] — сравнение элементов
jle lesser ;если mas[i] меньше, то обмен не нужен и
;переход на продвижение далее по массиву
;иначе tmp=mas[i], mas[i]=mas[j], mas[j]=tmp:
;tmp=mas[i]
movsxebx,i ;ebx=i
shl ebx,1 ;умножаем на 2, так как элементы — слова
mov tmp,ax ;tmp=mas[i]

;mas[i]=mas[j]
movsxebx,j ;bx=j
shl ebx,1 ;умножаем на 2, так как элементы — слова
mov ax,mas[ebx] ;ax=mas[j]
movsxebx,i ;bx=i
shl ebx,1 ;умножаем на 2, так как элементы — слова
mov mas[ebx],ax ;mas[i]=mas[j]

;mas[j]=tmp
movsxebx,j ;bx=j
shl ebx,1 ;умножаем на 2, так как элементы — слова
mov ax,tmp ;ax=tmp
mov mas[ebx],ax ;mas[j]=tmp
lesser: ;продвижение далее по массиву во внутреннем цикле
dec j ;j-
;тело цикла по j
cycl_j:
mov ax,j ;ax=j
cmp ax,i ;сравнить j ? i
Arhitectura calculatoarelor (lucrări de laborator ) 87
jg exchange ;если j>i, то переход на обмен
;иначе на внешний цикл по i
inc i ;i+
cmp i,n ;сравнить i ? n — прошли до конца массива
jl internal ;если i
M2: ;вывод отсортированного массива

mov edx,OFFSET mes2 ;вывод сообщения mes2


call WriteString

prepare:
mov ecx,10
mov esi,0
show: ;вывод значения элемента на экран
movsx eax,mas[esi]
call WriteDec

add esi,2
loop show
quit:
call crlf
exit ; выход
main ENDP
END main ;конец программы

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


программа показывает использование средств ассемблера для решения подобного рода задач. В
программе два цикла. Внешний цикл определяет позицию в массиве очередного элемента, с
которым производится попарное сравнение элементов правой части массива (относительно
этого элемента). За каждую итерацию внешнего цикла на месте этого очередного элемента
оказывается меньший элемент из правой части массива (если он есть).

5.6 Задание к лабораторной работе


Необходимо выполнить ассемблирование вышеприведенных примеров (примеры 5.1-5.5) и
проанализировать их в пошаговом режиме Debug.

5.7 Варианты индивидуальных заданий (применяйте MACRO)


1. Подсчитать количество появлений двух символов, к примеру «ХХ», в некотором массиве
символов. Вывести на монитор массив и результат.
2. Предполагается некоторый текст, состоящий из последовательностей символов, каждая
последовательность имеет вид текстовой строки. Необходимо объединить все строки и разделить
их при помощи пробела. Вывести все массивы на монитор.
3. Предполагаются две последовательности символов mas1, mas2 и некий символ simv.
Произвести поиск и удаление simv из массива mas1 и отсортированный массив занести в mas2.
Вывести все массивы на монитор.
4. Предполагается некоторый текст, состоящий из последовательностей символов, каждая
последовательность имеет вид текстовой строки. Необходимо объединить все строки и удалить все
символы $. Вывести все массивы на монитор.
5. Предполагаются две последовательности символов mas1, mas2 и некий символ simv.
Произвести поиск simv в массиве mas2 и после каждого найденного simv занести mas1 в mas2.
Вывести все массивы на монитор.
6. Предполагаются три последовательности символов mas1, mas2, mas3 (количество символов
в каждом массиве более 50). Необходимо объединить последовательно все массивы и занести в
mas4 размерностью в 140 символов. Оставшиеся символы теряются. Вывести все массивы на
монитор.
Arhitectura calculatoarelor (lucrări de laborator ) 88
7. Предполагается последовательность mas1 из n символов (mas=1, 2,…, i,…, k,…, n).
Необходимо: а) объединить последовательности от 1 до i и от k до n и занести в mas2;
б) последовательность от i до k занести в mas3. Вывести все массивы на экран.
8. Разработайте программу, которая изменяет последовательное расположение элементов
некоторого массива, заканчивающегося символом ноль (код ASCII 00h), на обратное. Вывести
все массивы на экран.
9. Предполагается таблица со 16-битными входами. Каждый вход может иметь одну из
следующих структур:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 0 │ │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 1 │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘

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


последовательности строк и в каждой строке выводит шестнадцатеричное значение полей одного
входа. Пример: если таблица содержит два входа со структурой:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 0 │ 1 1 1 │ 1 1 0│ 1 0 1 │ 0 1 0 │ 1 0 0 │
├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤
│ 1 │ 0 1 0 1 0 │ 0 1 1 0 1 │ 1 0 1 1 1 │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘

То выводиться будут следующие строки:


0 7 6 5 2 4
1 0A 0D 17

5.8 Содержание отчета.


Отчёт по лабораторной работе должен содержать программы 3-x примеров (*.asm), а т
акже разработанные программы индивидуального задания из п. 5.7 (*.asm).

5.9 Задачи для неаудиторной работы


Вопросы для подготовки к лабораторной работе 6: Обьясните последовательность вызова
одной из функций BIOS.
Arhitectura calculatoarelor (lucrări de laborator ) 89

Лабораторная работа № 6

Применение сервисных функций BIOS для работы с экраном и


клавиатурой

1.1 Цель работы - изучение функций BIOS


1.2 Методические рекомендации

1.2.1 Введение

Все возможности видеосистемы компьютера можно реализовать с помощью


видеофункций BIOS прерывания int 10h. Прерывание int 10h обеспечивает: смену видеорежима
(текстовый или графический); вывод символьной и текстовой информации; смену шрифтов,
настройку цветовой палитры, работу с графическим изображением. Программирование
видеосистемы с помощью средств BIOS более громоздко, однако большие возможности и
высокая скорость вывода обуславливают широкое использование этого метода в прикладных
программах.
В данной работе рассматриваются функции BIOS для обслуживания видеосистемы
компьютера, а также функции для работы с клавиатурой. Перечислим функции, являющиеся
предметом рассмотрения в лабораторной работе.
Int 10h:
функция 00h – установка видеорежима;
функция 02h – установка позиции курсора;
функция 03h – считывание позиции и размера курсора;
функция 05h – установка видеостраницы;
функция 06h (07h) – инициализация или прокрутка окна вверх (вниз);
функция 08h – чтение символа и атрибута в позиции курсора;
функция 09h – запись символа и атрибута в позицию курсора;
функция 0Ah – запись символа в позицию курсора с текущим атрибутом;
функция 0Сh – запись графической точки;
функция 0Eh – запись символа в режиме телетайпа с текущим атрибутом;
функция 0Fh – получить режим дисплея;
функция 1003h – переключение назначения старшего бита байта атрибута:

мерцание/яркость,

функция 13h – запись строки с заданным атрибутом в режиме телетайпа.


Int 16h:
Arhitectura calculatoarelor (lucrări de laborator ) 90
функция 00h (10h) – чтение символа с клавиатуры с ожиданием;
функция 01h (11h) – проверка буфера клавиатуры на наличие в нём символа;
функция 02h (12h) – получение флагов (расширенной) клавиатуры.
Int 15h, функция 86h – задержка.
Int 1Ah, функция 00h – получение системного времени.

1.2.2 ПРЯМОЕ ПРОГРАММИРОВАНИЕ ВИДЕОБУФЕРА В ТЕКСТОВОМ РЕЖИМЕ


Современные видеоконтроллеры поддерживают разнообразные текстовые и графические
режимы. Текстовые режимы различаются по разрешению (число отображаемых символов по
горизонтали и вертикали) и цветовой палитре (монохромный или 16-цветный режим). Для
графических режимов основным признаком классификации является количество
одновременно отображаемых цветов и, соответственно, количество бит видеопамяти,
отводимое на каждую точку (пиксел) изображения. Различают следующие типы графических
режимов:
– монохромный (1-битное кодирование);
– 16-цветный EGA/VGA (4-битное кодирование);
– 256-цветный SVGA (8-битное кодирование);
– HiColor (16-битное кодирование);
– TrueColor (24-битное / 32-битное кодирование).
Графические режимы VGA сильно устарели, а текстовые продолжают успешно применяться
(см. табл. 1.1 п. 1.2.3).
Всё, что изображено на мониторе – графика, текст – одновременно присутствует в памяти,
встроенной в видеоадаптер. Для того чтобы изображение появилось на мониторе, оно
должно быть записано в память видеоадаптера. В текстовом режиме для VGA-совместимых
систем для видеопамяти отводится адресное пространство (исключая 7-й видеорежим с
монохромным адаптером), начинающееся с логического адреса B800h:0000h и
заканчивающееся адресом BF00h:0FFFh. Данная область разбивается на 8 секторов по числу
видеостраниц (4 Кбайта на страницу). Таким образом, постраничное деление адресного
пространства видеопамяти в текстовом режиме имеет следующий вид:
– B800h:0000h – страница 0, смещение в диапазоне 0000h – 0FFFh
– B900h:0000h – страница 1, смещение в диапазоне 0000h – 0FFFh
– ...........
– BF00h:0000h – страница 7, смещение в диапазоне 0000h – 0FFFh
На экране отображается видеобуфер, соответствующий активной странице. В текстовых
режимах для изображения каждого символа отводится 2 байта: байт с ASCII-кодом символа и
байт с его атрибутом. При этом по адресу B800h:0000h находится байт с кодом символа
(левый верхний угол экрана), а в B800h:0001h – атрибут этого символа; B800h:0002h – код
второго символа, а в B800h:0003h – атрибут второго символа и т.д. Вообще при
формировании изображения непосредственно в видеобуфере, в обход программ DOS и BIOS,
все управляющие коды ASCII теряют свои управляющие функции и отображаются в виде
соответствующих символов. Структура байта атрибутов приведена на рис. 1.1.

76543210

Цвет фона Цвет символа


Яркость символа
Мерцание символа/Яркость фона
Рисунок 1.1 - Структура байта атрибутов

Из рис. 1.1 следует, что каждый символ может принимать любой из 16 возможных цветов,
определяемых сочетанием младших 4-х битов. Биты 4-6 байта атрибутов задают цвет фона
под данным символом. Последний бит 7, в зависимости от режима видеоадаптера,
Arhitectura calculatoarelor (lucrări de laborator ) 91
определяет либо яркость фона под данным символом (тогда фон также может принимать 16
разных цветов), либо мерцание символа (устанавливается DOS по умолчанию).
При загрузке машины устанавливается стандартная палитра, коды цветов которой приведены
в табл. 1.1. Рассмотрим некоторые примеры. Так, в режиме мерцания значение старшего
полубайта атрибута 8h обозначает не серый фон, а чёрный при мерцающем символе, цвет
которого по-прежнему определяется младшим полубайтом; значение старшего полубайта
0Ch – красный фон при мерцающем символе. Переключение назначения бита 7
осуществляется подфункцией 03h функции 10h прерывания int 10h.
Т а б л и ц а 1.1 - Коды цветов стандартной палитры
Код Цвет Код Цвет
0h Чёрный 8h Серый
1h Синий 9h Голубой
2h Зелёный 0Ah Салатовый
3h Бирюзовый 0Bh Светло-бирюзовый
4h Красный 0Ch Розовый
5h Фиолетовый 0Dh Светло-фиолетовый
6h Коричневый 0Eh Жёлтый
7h Белый 0Fh Ярко- белый

Двухбайтовые коды символов записываются в видеобуфер в том порядке, в каком они


должны появиться на экране: первые 80*2 байт соответствуют первой строке экрана, вторые
80*2 байт – второй и т.д. При этом переход на следующую строку экрана определяется не
управляющими кодами возврата каретки и перевода строки, а размещением кода в другом
месте видеобуфера. Для того чтобы из программы получить доступ к видеобуферу, надо
занести в один из сегментных регистров данных сегментный адрес видеобуфера. После
этого, задавая те или иные смещения, можно выполнить запись в любые места (ячейки)
видеобуфера. Вычислить смещение ячейки в координатах "строка-столбец" (row, clm) можно
так:
VidAdd r= (row*160) + (clm*2)
При большом объёме выводимых данных, информационный кадр формируется заранее в
буфере пользователя, располагающегося в сегменте данных программы.

Листинг 1.1 - Запись строки в видеобуфер 0-страницы.


;Очистка экрана

;Настроим сегментный регистр ES на страницу 0 видеобуфера, а ds на сегмент данных
mov ax,0B800h
mov es,ax
;Перешлём в видеобуфер строку символов, настроив соответствующим образом
;регистры si, di и cx
mov si,offset msg ;Смещение источника
mov di,160*12+36*2 ;Смещение приёмника (36 столбец 13 -ой строки),
mov cx,msglen ;Число пересылаемых байт
cld ;Просмотр вперёд
rep movsb ;)* ;Переслать строку символов с атрибутами в видеобуфер
;Остановим программу для наблюдения результата (иначе после завершения программы
;запрос BIOS на ввод команды может затереть выведенную информацию)
mov ah,0
int 16h

;Поля данных в сегменте данных программы. Символы и атрибуты: 0B0h – cветло-
;бирюзовый по чёрному, 0E4h –красный по жёлтому
Arhitectura calculatoarelor (lucrări de laborator ) 92
msg db ‘*’,0B0h,’T’,0E4h,’E’,0E4,’S’,0E4,’T’,0E4,’*’,0B0h
msglen = $-msg
В данном фрагменте программы символьные коды выводимого сообщения перемежаются с
их атрибутами. Такой способ формирования полей данных, предназначенных для прямой
записи в видеопамять, становится громоздким, однако его можно существенно упростить,
если выводимые символы имеют одни и те же атрибуты. Так, если мы хотим осуществить
вывод символов текста из сегмента данных с единственным атрибутом 0E4h, то нам нужно
просто заменить одну командную строку, отмеченную в выше приведённом фрагменте
символом "*)", на три. При этом задание строки данных приобретёт привычный для нас вид.

mov si,offset msg ;Смещение источника
mov di,160*12+36*2 ;Смещение приёмника (36 столбец 13 -ой строки),
mov cx,msglen ;Число пересылаемых байт
cld ;Просмотр вперёд
mov ah,0E4h ;Атрибут выводимых символов 0E4h – красный по жёлтому
cycle: lodsb ;Загрузка в al очередного символа (al ← ds:si)
stosw ;Выгрузка “символ + атрибут” из ах в видеобуфер (ax→es:di)
loop cycle ;Повторить msglen раз

;Поля данных в сегменте данных программы.
msg db ‘*TEST*’
msglen = $-msg

Изложенный выше способ вывода текста форматируется длиной видеостроки без учёта
символов переноса или отступов от левой границы. Внесение элементарных правил
текстового редактора в процедуру вывода сильно усложнит программу. В этом случае для
вывода сообщений целесообразно использовать функции BIOS.
Разработка структуры программ, осуществляющих просмотр произвольных видеостраниц, на
которые предварительно записана информация способом прямого программирования
видеобуфера, удобно производить с применением функции 05h int 10h BIOS (п. 8.2.3.2).

1.2.3 СПРАВОЧНЫЕ ДАННЫЕ ПО ФУНКЦИЯМ BIOS

Прерывание int 10h. Видеофункции BIOS


 Функция 00h. Установка видеорежима (табл. 1.2) текущей видеостраницы с очисткой
экрана (быстрая очистка экрана реализуется функцией 06h и 07h).
Вызов: AH = 00h,
Al = видеорежим (код режима задаётся в младших 7 битах, установка в 1 старшего бита
запрещает очистку экрана).
Вызов разрушает регистры AX, BP, SI, и DI.

Т а б л и ц а 1.2 - Текстовые видеорежимы и страницы в стандарте VGA


Режим Тип Разрешение Цвет Размер знака Адрес Страницы
0 text 40x25 16 9x16 B8000 0–7
полутонов
1 text 40x25 16/8 9x16 B8000 0–7
2 text 80x25 16 9x16 B8000 0–7
полутонов
3 text 80x25 16/8 9x16 B8000 0–7
6 graphic 640x200 / 80x25 2 8x8 B8000 0
7 text 80x25 3 (Mono) 9x16 B0000 0
10h graphic 640x350 / 80x25 4 или 16 8x14 A0000 0–1
11h graphic 640x480 / 80x30 2 (Mono) 8x16 A0000 0
12h graphic 640x480 / 80x30 16 8x16 A0000 0
Arhitectura calculatoarelor (lucrări de laborator ) 93
13h graphic 640x480 / 80x30 256 8x8 A0000 0

По умолчанию в DOS используется режим 3 (впрочем, корректно оформленная программа


должна выполнять проверку или установку требуемого текстового режима с последующим
восстановлением прежнего).
 Функция 02h. Установка позиции курсора.
Задаёт положение курсора на экране в текстовых координатах, с которых в дальнейшем будет
выводиться текст. Отсчёт номера строки и столбца ведётся от верхнего левого угла. Курсор
можно установить, как в текстовом, так и в графическом режиме, однако, в графическом
режиме курсор не виден. BIOS поддерживает до восьми независимых курсоров – по одному
на каждую страницу (см. табл. 3.2) независимо от того, какая страница является активной.
Функцию 02h BIOS можно использовать в комбинации с функциями DOS для организации
вывода на экран.
Вызов: AH = 02h; BH = номер страницы (0,1,...7), обычно 0;
DH = строка; DL = столбец.
Вызов разрушает регистры AX, BP, SI и DI.
 Функция 03h. Считывание позиции и размера курсора.
Возвращает текущие координаты состояния курсора на выбранной странице. Это даёт
возможность временно перейти для работы на другое место экрана, а затем вернуться на
старое место. Функцию 03h BIOS можно использовать в комбинации с функциями DOS для
организации вывода на экран.
Вызов: AH = 03h, BH = номер страницы (0,1,...7), обычно 0.
Возврат: DH, DL = строка и столбец текущей позиции курсора,
CH, CL = первая и последняя строки развёртки курсора.
Вызов разрушает регистры AX, BP, SI и DI.

 Функция 05h. Установка видеостраницы.


Устанавливает активную видеостраницу (как текстовую, так и графическую).
Вызов: AH= 05h, AL= номер страницы (0,...,7).
Вызов разрушает регистры AX, BP, SI и DI.
Программа, устанавливающая страницу, отличную от текущей, обязана по окончании
работы восстанавливать исходную.
 Функция 06h (07h). Инициализация или прокрутка окна вверх (вниз).
Инициализирует окно с указанными координатами, пробелами ASCII с заданным атрибутом
(AL = 0), или прокручивает содержимое окна вверх (вниз) на заданное число строк (AL =
число строк). При прокрутке появляющиеся снизу (сверху) строки заполняются пробелами
ASCII с заданным атрибутом. Функцию удобно использовать для быстрой очистки экрана или
некоторого прямоугольного окна.
Вызов: AH = 06h(07h);
AL = 0 – очистка, AL = N (N >0) – прокрутка на N строк;
BH = атрибут символов в окне;
CH, CL = координаты строки и столбца (Y,X) левого верхнего угла;
DH, DL = координаты строки и столбца (Y,X) правого нижнего угла.
Вызов разрушает регистры AX, BP, SI, и DI.
 Функция 08h. Чтение символа и атрибута в текущей позиции курсора на выбранной
странице.
Вызов: AH = 08h, BH = номер страницы (0,...,7), обычно 0.
Возврат: AH = атрибут символа, AL = ASCII-код символа.
Вызов разрушает регистры BP, SI и DI.
 Функция 09h. Запись символа с заданным атрибутом на экран в позицию курсора.
Действует как в графическом, так и в текстовом режимах. В графическом режиме символы не
Arhitectura calculatoarelor (lucrări de laborator ) 94
должны переходить на следующую строку. Все коды в AL рассматриваются как символьные и
не управляют положением курсора. После вывода символа курсор смещается к следующей
позиции функцией 02h. Коэффициент повторения позволяет выводить строки одинаковых
символов. В текстовом режиме символ выводится с указанным в BL атрибутом. В графическом –
содержимое BL влияет только на цвет символа, но не на фон под ним. Графическое изображение
под знакоместом затирается.
Вызов: AH =09h, AL = ASCII-код символа,
BL = атрибут символа (текстовый режим) или только цвет символа (графический
режим),
BH = номер страницы (0,1,...7), CX = коэффициент повторения.
Вызов разрушает регистры AX, BP, SI и DI.
 Функция 0Ah. Запись символа с текущим атрибутом на экран в позицию курсора.
Функция действует как в графическом, так и в текстовом режимах. Символ принимает атрибут,
установленный ранее для этой позиции. Все ASCII-коды в AL рассматриваются как символьные
и не управляют положением курсора (также как и в функции 09h). После вывода символа
курсор смещается к следующей позиции функцией 02h.
Вызов: AH = 0Ah, AL = ASCII-код символа,
BH = номер страницы (0,1,...7), CX = коэффициент повторения.
Вызов разрушает регистры AX, BP, SI и DI.
 Функция 0Eh. Запись символа с текущим атрибутом в режиме телетайпа.
Записывает символ ASCII в позицию курсора (предварительно установленную функцией 02h)
на активной странице и смещает курсор к следующей позиции. Коды ASCII: 07h – звонок
(BEL), 08h – шаг назад (BS), 0Dh – возврат каретки (CR), 0Ah – перевод строки (LF),
рассматриваются как управляющие и выполняются соответствующие им действия.
Остальные управляющие коды рассматриваются как символы и выводятся на экран.
Действует автоматический перевод курсора на следующую строку после завершения
предыдущей, а также прокрутка экрана вверх на 1 строку после заполнения самой нижней.
Вызов: AH = 0Eh, AL = ASCII-код символа,
BL = цвет символа (только для графического режима),
BH = номер страницы (0,1,...7), по умолчанию действует активная страница.
 Функция 0Fh. Получить режим дисплея и номер текущей страницы.
Вызов: AH = 0Fh.
Возврат: AL = режим дисплея, AH = ширина экрана в текстовом формате
BH =номер активной страницы.
Вызов разрушает регистры BP, SI и DI.
Пример. Процедура установки позиции курсора на текущей странице.
Вход: dh = строка (0 – 25), dl = столбец (0 – 79)
Proc SetCursor
....... ;Сохранить регистры (по необходимости)
mov ah,0Fh
Int 10h
mov ah,02h
Int 10h
.......... ;Восстановить регистры
SetCursor Endp
 Функция 10h. Подфункция 03h. Переключение бита "мерцание/яркость".
Определяет назначение старшего бита 7 атрибута символа: мерцание символа или
повышенная яркость фона.
Вызов: AX = 1003h, BL = назначение 7-го бита атрибута:
0 – повышенная яркость, 1 – мерцание (устанавливается по умолчанию).
Функция воздействует сразу на все символы экрана, у которых установлен старший бит
атрибута фона.
 Функция 13h. Запись строки символов с заданными атрибутами.
Arhitectura calculatoarelor (lucrări de laborator ) 95
Записывает строку в текущую страницу видеобуфера, начиная с указанной позиции. Коды
ASCII: 07h – звонок, 08h – шаг назад, 0Ah – перевод строки,
0Dh – возврат каретки, рассматриваются как управляющие, остальные – как символьные.
Вызов: AH = 13h, AL = режим записи:
0 – атрибут символа в BL, строка содержит только коды символов, после записи курсор
принимает исходное положение (т.е. вывод следующей строки, если не изменить позицию
курсора, начинается с изначально установленной позиции);
1 – отличается от режима 0 тем, что после записи курсор остаётся в конце строки;
2 – строка содержит попеременно коды символов и атрибутов (т.е. каждый символ
описывается 2 байтами – ASCII-кодом и атрибутом), после записи курсор принимает
исходное положение;
3 – отличается от режима 2 тем, что по окончании вывода курсор остаётся в конце строки.
BH = номер страницы (0,1,...7), BL = атрибут для режимов 0 и 1,
CX = длина символьной строки (в длину входят только коды символов, но не байты
атрибутов),
DX = DH.DL = координаты курсора (строка, столбец) в исходной точке вывода строки на
экране,
ES:BP = адрес начала строки в памяти.

Пример программы выводящей зеленый прямоугольник на экран:


Include irvine16.inc
.code
main proc
start: mov ax,@data
mov ds,ax

mov ah,00h ; устанавливаем видеорежим


mov al,12h; разрешение 640 x480
int 10h ; вызов BIOS

mov ah,0ch ; functia – запись графической точки

mov bh,0 ;страница video (базовая 0 )


mov cx,0 ; столбец
mov dx,0 ; строка

m2: mov al,0ah ; цвет точки (табл. 1.1)


int 10h
inc cx ; след. точка
cmp cx,210
jne m2
inc dx ; след. строка
xor cx,cx
cmp dx,100
jne m2

mov ah,0; click (pauza)


int 16h

mov ax,03 ; текстовый режим


int 10h

Exit
Arhitectura calculatoarelor (lucrări de laborator ) 96
main ENDP ; завершение процедуры main
END main ;завершение программы/ точка входа в программу

1.3 ВАРИАНТЫ ИНДИВИДУАЛЬНОГО ЗАДАНИЯ


Вывести на экран флаг некоторого государства. Используйте DOSBox.

Оценить