Поиск по сайту:


«Assembler IBM PC 6. Лабораторная работа № 1. Ассемблирование и отладка готовых программ. Изучение системы команд и способов адресации операндов»

Файл: 4 КБ
Поделиться:

6.1. ЦЕЛЕВЫЕ УСТАНОВКИ

·     Знакомство с полным циклом создания ассемблерной программы.

·     Знакомство с отладчиком Turbo Debugger.

·     Знакомство с форматами команд процессоров i80x86 и способами адресации операндов.

·     Принципы кодирования команд.

6.2. МЕТОДИЧЕСКИЕ РЕКОМЕНДАЦИИ

6.2.1. ЭТАПЫ СОЗДАНИЯ ПРОГРАММЫ

Наибольшей популярностью у программистов на языке ассемблера для процессоров i80x86 пользуются два пакета:

¨  Макроассемблер Masm фирмы Microsoft;

¨  Turbo Assembler фирмы Borland (Tasm), поддерживающий два синтаксических стандарта или режима:

·     Masm (по умолчанию), совместимый с макроассемблером фирмы Microsoft;

·     Ideal, режим улучшенного синтаксиса фирмы Borland.

В лабораторных работах используется пакет Borland Tasm 5.0.

Разработка программы на языке ассемблера включает четыре этапа [6, 8].

1-й этап. Подготовка исходного текста программы и оформление его в виде текстового файла (одного или нескольких) с помощью какого-нибудь редактора в формате DOS с расширением .asm.

2-й этап. Ассемблирование программы с применением транслятора Tasm, результатом которого является объектный файл с расширением obj. Когда программа состоит из нескольких файлов (модулей), то их ассемблирование производится независимо друг от друга. Если в процессе трансляции будут обнаружены ошибки, то объектный файл не создаётся, а формируется сообщение об ошибках. Ошибки устраняются, после чего трансляция повторяется. Объектный файл (двоично-кодированное представление программы) не может быть запущен на исполнение, так как в нём не содержится информация о загрузке сегментов программы в памяти компьютера.

3-й этап. Компоновка программы производится компоновщиком (редактором связей) Turbo Linker и заключается в объединении объектных модулей в один исполняемый файл с назначением стартового адреса программы. Исполняемый файл имеет расширение exe. 2-й и 3-й этапы определяют процесс подготовки исполнительного файла программы, называемого трансляцией.

4-й этап состоит в отладке программы с использованием отладчика Turbo Debugger, который будет являться основным инструментом при изучении форматов команд, их кодирования, а также представления переменных программы в памяти.

6.2.2. ПУТЬ ВЫПОЛНЕНИЯ КОМАНДЫ

Если в ответ на приглашение DOS ввести команду или имя исполняемого файла (без расширения) и нажать клавишу Enter, то путь поиска системой (командным процессором Command) необходимого объекта будет следующим.

1.   Проверяется, не ввёл ли пользователь имя внутренней команды DOS. Если введена внутренняя команда, она будет выполнена.

2.   Проверяется текущая директория на предмет выявления программного файла с именем и расширением .com или .exe. и только затем ищется командный файл с расширением .bat. Найденный файл исполняется.

3.   Если ничего из перечисленного в пп. 1 и 2 не найдено, то система начнёт поиск по путям, записанным в файле Autoexec.bat (Windows 9x) или в Autoexec.nt (Windows 2000). Оператор пути может быть примерно таким (допустим, что в компьютере используются ещё программы-оболочки FAR и Dos Navigator):

Path=с:\;с:\windows\command;c\program files\far;f:\tasm5\bin;f:\dn     для Windows 9x

Path= f:\tasm5\bin;f:\dn                                                                            для Windows 2000

4.   Если система не находит ни команду, ни имя исполняемого файла, то выводится соответствующее сообщение.

Уменьшает время поиска, таким образом, ввод расширения имени файла. Если же, кроме всего прочего, в командной строке указывается полный путь поиска, то он предельно упрощается.

6.2.3. ТРАНСЛЯЦИЯ ПРОГРАММЫ. ОПЦИИ КОМАНДНОЙ СТРОКИ

Рассмотрим процесс трансляции программы на примере простой интерактивной программы Hello.asm (см. приложение П.1.2 к работе № 1). Для ассемблирования файла Hello.asm в командной строке наберите: tasm hello.asm и нажмите клавишу Enter. Поскольку Вы не задали другое имя для объектного файла, то будет создан объектный файл с этим же именем hello.obj. Заметим, что расширение имени файла .asm вводить не требуется, т.к. TASM принимает это по умолчанию. На экране вы увидите следующее:

Turbo Assembler Version 4.1 Copyright (C) 1988,1996 Borland International
Assembling file:            Hello.asm

Error messages:           None

Warning messages: None

Passes               1

Remaining memory: ***K

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

Для компоновки программы введите в командную строку: tlink hello.obj.

Здесь, также как и при ассемблировании, расширение имени obj не является обязательным. По завершению компоновки будет сформирован файл hello.exe с выводом на экран сообщения.

Turbo Linker Version 7.1. 30.1 Copyright (C) 1987,1996 Borland International

Теперь программу hello.exe можно запустить на исполнение, результатом которого будет вывод на экран сообщения:

Это время после полудня? (Да/Нет-Y/N)

Курсор будет мерцать после последнего символа в ожидании ввода ответа. Введите букву y. Программа ответит:

Здравствуйте!

Если будет введена строчная буква n, то программа ответит:

Доброе утро!

В процессе ассемблирования или компоновки вы можете выбирать различные исполнения процесса, которые задаются опциями в командной строке Tasm или Tlink. Для вывода списка опций командной строки Turbo Assembler наберите tasm и нажмите <Enter>; то же самое проделайте с командой tlink.

Опции описываются одной или несколькими буквами. Для задания опции наберите косую черту (или дефис) и соответствующую букву между командой tasm или tlink и именем программы, которую вы ассемблируете либо компонуете. Например, для ассемблирования программы Hello.asm и получения файла с листингом (файл, в котором содержится описание процесса ассемблирования), используйте команду:tasm/l hello. Опции разрешается набирать как прописными, так и строчными буквами. Исполните эту команду и затем рассмотрите файл с листингом Hello.lst, используя текстовый редактор. В листинге каждая строка начинается с номера, затем следуют байты объектного кода и, наконец, собственно текст программы. Кроме того, TASM выводит в файле листинга таблицу идентификаторов, где содержится информация о метках и сегментах, включая значение и тип каждой метки и атрибуты каждого сегмента. При ассемблировании программы вы можете использовать в одной командной строке несколько опций, разделяя их косыми чертами. Рассмотрим некоторые примеры для команды tasm:

tasm/l/c hello – команда дополняет файл листинга таблицей перекрёстных ссылок, в которой указывается, где была определена каждая метка и где на неё есть ссылка;

tasm/l/n hello – исключает таблицу идентификаторов из листинга;

tasm/ml         – включает различие прописных и строчных символов в пользовательских идентификаторах;

tasm/zi hello  – команда добавляет в Hello.obj информацию, необходимую для использования отладчика Turbo Debugger.

Для команды tlink:

tlink/v hello   – опция /включает в Hello.exe информацию, необходимую для последующего открытия этого файла в отладчике Turbo Debugger;

tlink/m hello  – опция /m приводит к созданию файла отображения или файла загрузки Hello.map. В нём перечисляются имена, адреса загрузки и размеры всех сегментов, входящих в программу;

tlink/x/v hello – команда даёт возможность загрузить Hello.exe в отладчик Turbo Debugger, запрещает создание (опция /x) файла отображения.

Tlink/3 <file_name.obj>  – компоновщик создаёт файл типа .exe с поддержкой 32-битного кода.

Tlink/t <file_name.obj>   – опция /t создаёт файл типа .com.

6.2.4. СТРУКТУРА ПРОГРАММЫ ДЛЯ .EXE- И .COM-ФАЙЛОВ.
ОБРАЗ ПРОГРАММЫ В ПАМЯТИ

Программы, выполненные под управлением DOS, могут принадлежать к одному из двух типов, которым соответствуют расширения имён программных файлов .com и .exe. Основное различие этих программ заключается в том, что программы типа .com состоят из единственного сегмента объёмом в 64 Кбайт, в котором размещаются программный код, данные и стек, а в программах типа .exe для каждого из них выделяются отдельные сегменты. Управление сегментами – один из наиболее сложных аспектов программирования на языке ассемблера. При этом ассемблер имеет не один, а целых два набора директив управления сегментами [5, 6, 8, 13, 14]. Первый набор, включающий упрощенные директивы определения сегментов, позволяет достаточно просто ими управлять и идеально подходит для компоновки ассемблерных модулей с языками высокого уровня (Pascal и Си). Второй набор, включающий стандартные директивы определения сегментов, предназначен для построения сложных ассемблерных программ. В данном лабораторном практикуме, как правило, применяются упрощенные директивы, введение которых возможно лишь с одновременным указанием используемой модели памяти. В свою очередь, модель памяти неявно задаёт атрибуты упрощенных директив, определяющих действия компоновщика Turbo Linker при формировании исполнительного файла программы (см. табл. 1.1). Дадим краткую характеристику указанных в табл. 1.1 атрибутов.

Таблица 1.1

Сегменты упрощенной модели памяти Small

Директива

Имя

Выравнивание

Объединение

Класс

Группа

Codeseg

_Text

Word

Public

'Code'

 

Dataseg

_Data

Word

Public

'Data'

Dgroup

Stack

Stack

Para

Stack

'Stack'

Dgroup

 

 

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

·     Имя – идентификатор данного сегмента, используемый при назначении адреса и комбинировании (объединении) с другими сегментами.

·     Выравнивание – указывает граничные значения на начало сегмента. В процессе ассемблирования, если текущая позиция в начале сегмента не удовлетворяет установленному атрибуту, счётчик адреса увеличивается на соответствующую величину, смещая начало сегмента в область старших адресов (word – начало сегмента должно иметь чётный адрес, para – чётный адрес, кратный параграфу, т.е. 16 бит)

·     Объединение – устанавливает правила объединения нескольких сегментов с одинаковым именем. Параметр Public приводит к последовательному объединению сегментов с одинаковым именем в один большой сегмент, имеющий адрес первого из объединяемых сегментов.

·     Класс – выполняет роль категории сегмента. Все сегменты одинакового класса во время работы компоновщика физически располагаются друг за другом в памяти

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

Наибольшее применение в ассемблерных программах с расширением .exe нашла малая модель памяти Model small, предусматривающая размещение структурных частей программы в двух сегментах:

·     сегмент кода программы (64 Кбайт),

·     сегмент данных и стека, принадлежащих одной группе Dgroup (64 Кбайт).

Шаблон программы для exe-файлов с подробными комментариями представлен на рис. 1.1. Здесь отметим лишь оформление начала и конца кодового сегмента. Для того чтобы понять, почему кодовый сегмент начинается с загрузки сегментного регистра данных ds, а не с загрузки сегментного регистра cs, необходимо рассмотреть функции DOS при загрузке программы в память.

 

%Title "Имя EXE-файла программы"

          Ideal                    ;Переводит Turbo Assembler в режим Ideal

          Model small        ;Директива описания модели памяти. Вводит упрощенные директивы

                                      ;управления сегментами

          P486N                 ;Разрешает инструкции старших поколений процессоров. При её

                                      ;отсутствии действует по умолчанию P8086. Разрешает использование

                                      ;32 – разрядных регистров для адресации и хранения данных

          Stack 256            ;Резервирует пространство для стека программы (значение в байтах,

                                      ;следующее за директивой)

          ;1. Здесь следует располагать макроопределения EQU и =

          ;2. Вставьте здесь директиву Include "filename"

          Dataseg              ;Начало сегмента инициализированных данных. Допускается также

                                      ;расположение здесь и неинициализированных данных, начальные

                                      ;значения которых неизвестны на момент запуска программы

          ;1. Здесь описываются переменные с помощью директив DB,DW и т. п.

          ;2. Здесь опишите все переменные типа Extrn

          Codeseg              ;Начало сегмента кода, т.е. части программы, содержащей команды

                                      ; процессора.

          ;Здесь определите все подпрограммы типа Extrn

          Start:                    mov ax, @data            ;Точка начала исполнения программы

              mov ds,ax                    ;Установка в регистре DS адреса сегмента данных

          ;Здесь располагается программа, вызовы подпрограмм и т. п.

          Exit:                      mov ax,4C00h             ;Функция DOS: Выход из программы

                                      int 21h                         ;Вызов DOS. Останов программы

          End Start                                                 ;Директива конца программы/точки входа. Текст,

          ;расположенный после этой строки будет игнорироваться ассемблером.

Рис. 1.1. Шаблон структуры программы типа .exe

Образ программы в памяти [Л: 4, 12, 13, 14], представленный на рис. 1.2[2], начинается с префикса программного сегмента PSP (Program Segment Prefix)PSP всегда имеет размер 256 байтов и содержит данные, используемые операционной системой в процессе исполнения программы. Вслед за PSP располагаются сегменты программы. Сегментные регистры es и ds автоматически инициализируются на началоPSP. Это даёт возможность, при сохранении значения одного из регистров, обращаться к PSP в случае необходимости. В указатель команд ip загружается относительный адрес точки входа в программу (операнд директивы END), а в указатель стека sp -смещение конца сегмента стека. Таким образом, после загрузки программы в память, адресуемыми являются все сегменты, кроме сегмента данных. Использование директивы Model, делает доступным ассемблеру несколько служебных идентификаторов, к которым можно обращаться во время работы программы, чтобы получить информацию об адресах используемых сегментов. Например:

@code – 16-разрядный адрес сегмента кода,

@data – 16-разрядный адрес сегмента данных типа near,

@stack – 16-разрядный адрес сегмента стека.

 

 
 
 

 

 

 

 

 

 

 

 

 

 

 

 

Объём памяти, занимаемый программой: (138B-1373)*10h+100h=280h=640 байт

 

Рис. 1.2. Образ программы hello.exe в памяти, загруженной командой Td hello.exe

 

Вот поэтому-то имя предопределённого идентификатора @data и используется в первой паре команд кодового сегмента для загрузки сегмента данных.

Завершение программы означает передачу управления командному процессору DOS – Command.com, который выводит на экран системный запрос в ожидании следующей команды от оператора. Делается это с помощью функции DOS (ah = 4Ch). Эта функция требует единственного параметра – кода возврата, который и помещается программой в регистр al. Если этот код равен нулю, то исполнение программы прошло корректно, в противном случае была допущена ошибка в предшествующих обращениях программы к другим системным функциям DOS.

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

Шаблон программы типа .com приведён на рис. 1.3. При компоновке программы необходимо использовать опцию /t, которая указывает на создание файла .com вместо .exe.

Образ памяти программы типа .com показан на рис. 1.4. После загрузки программы в память все 4 сегментных регистра указывают на начало единственного сегмента – PSP. Указатель стека автоматически инициализируется числом 0FFFEh. Таким образом, независимо от фактического размера программы, ей выделяется 64 Кбайт адресного пространства, нижнюю часть которого занимает стек.

 

%Title "Имя com – файла программы"

                                      Ideal

                                      Model Tiny

                                      P486n

;Здесь следует располагать макроопределения EQU и =

                                      Dataseg                   ;Данные программы всё равно будут размещены

                                                                       ;компоновщиком после программного кода

;Здесь описываются переменные с помощью директив DB,DW и т. п.

                                      Codeseg

                                      Org 100h                  ;Стандартный адрес начала COM -программы.

                                                                       ;Резервируем место PSP

            Proc                   main                          ;Тело программы, оформленное в виде главной

                                                                       ;процедуры

;Здесь располагается программа, вызовы подпрограмм и т. п.

Exit:                   mov ax,4C00h          ;Функция DOS: Выход из программы.

                                      Int 21h                      ;Вызов DOS. Останов программы.

Endp                 main                          ;Конец процедуры

End                   main                          ;Директива конца программы/точки входа.

Рис. 1.3. Оболочка для .COM – файла программы

 

 
 
 

 

 

 

 

 

 

 

 

 

 

Рис. 1.4. Образ .com – программы в памяти

6.2.5. РАБОТА С ОТЛАДЧИКОМ TURBO DEBUGGER (TD)

Tasm умеет ассемблировать синтаксически правильные программы, но не понимает, что, собственно, эта программа делает. Часто программа работает не так, как, по вашему мнению, должна была бы работать. В такой ситуации может помочь TD-программа, разработанная для поиска и исправления логических ошибок [5, 6, 12, 14]. Подобно всем отладчикам TD может работать в режиме супервизора, беря на себя управление программой в режиме пошагового исполнения, кода программы. Можно при этом изменять значения операндов в памяти, а также значения регистров и флагов. TD используется и в качестве учителя при изучении форматов машинных команд процессора в различных режимах адресации операндов.

Чтобы показать, как использовать TD при изучении языка ассемблера, исследуем программу Hello под управлением отладчика. Произведём заново ассемблирование и компоновку программы с опциями, которые добавляют отладочную информацию в obj- и exe-файлы:

tasm/zi hello.asm

tlink/v hello.obj

td hello.exe

После выполнения последней команды на экране увидите окно Module с исходным текстом программы Hello.asm. Это окно номер 1. Просмотреть программу можно, используя клавиши управления курсором, передвигая его вверх и вниз по тексту.

Для получения другого представления программы войдите в меню Viev [обзор], выберите команду CPU и нажмите <F5> для распахивания окна на весь экран. В CPU-окне, состоящем из пяти областей, содержится в сокращённом виде исходный текст вашей программы, действительные машинные коды, находящиеся в памяти, значения регистров и флагов, стек и дамп байтов памяти. Для передвижения курсора из одной области в другую нажимайте клавишу <Tab>.

Область окна CPU, в которой находится курсор, считается активной. Нажатие клавиши <Alt+F10> вызывает появление локального меню для активной секции окна CPU. Перейдите в главную область окнаCPU и нажмите <Alt+F10>. Выберите команду Mixed (смесь), которая имеет три установки: No, Yes и Both. Режим Both (оба) устанавливается по умолчанию и является наилучшим способом просмотра, показывая в левой колонке байты машинного кода, а в правой – строки исходного текста программы.

Окно CPU используется для наблюдения за текущим состоянием процессора при пошаговом выполнении инструкций программы. Маленькая стрелка-треугольник слева от первой команды mov ax,@dataпоказывает, что она является следующей исполняемой командой. Для выполнения этой команды нажмите <F8>, стрелка перейдёт на следующую команду с изменённым значением регистра ax в окне регистров. Снова нажмите <F8>, для выполнения инструкции mov ds,ax. Обратите внимание, что значение в регистре ds стало таким же, как и в регистре ax, но произошло изменение сегментации дампа памяти (левая нижняя область) с регистра ds на регистр es.

Сделайте активной область с дампом памяти, нажмите <Alt+F10> и в появившемся локальном меню выберите команду Goto (переход). Появится заставка, в которой наберите ds:0000 и нажмите <Enter>. Теперь дамп памяти будет соответствовать массиву инициализированных данных вашей программы при их побайтовом представлении (ASCII-код символов переменной Promt, Good Morning и Good Afternoon). Для просмотра всего дампа используйте передвижение курсора с помощью стандартных клавиш. Объяснение этому факту следует из особенностей загрузки операционной системой MS-DOS в память программ с расширением exe (рис. 1.2).

Чтобы лучше разобраться с представлением переменной Promt в дампе памяти, войдите в меню Data (данные), выберите команду Inspect (проверка). В появившейся заставке наберите Promt и нажмите <Enter>. Раздвиньте с помощью мыши появившееся окно Inspecting Promt по вертикали на всю высоту экрана в правой его части. В левой колонке данного окна указывается номер элемента переменной массиваPromt[i], а в правой – значение ASCII-кода этого элемента. Найдите эти коды в дампе памяти, а именно:

          ds:0000                exCode=00h

ds:0001                Promt[0]='Э'=9Dh

ds:0002                Promt[1]='т'=E2h

          ds:0003                Promt[2]='о'=AEh

и т. д.

Продолжим покомандное исполнение программы с помощью клавиши <F8>, предварительно закрыв окно Inspecting Promt, щёлкнув мышью по кнопке закрытия. После выполнения каждой команды наблюдаем за изменением значений регистров.

Описанный выше процесс выполнения программы прерывается после исполнения команды прерывания DOS (Int 21,функция ah = 1) по вводу символа с клавиатуры на экран. TD переводит вас с окна CPUв окно пользователя User Screen, в котором появляется сообщение-запрос переменной Promt. Ответив на этот запрос (прописной или строчной буквой: Y или y), вы автоматически снова перейдёте в окно CPU, для дальнейшего покомандного выполнения программы. Переход, при необходимости, к окну пользователя от TD и обратно можно осуществить нажатием клавиш <Alt+F5>. Если вы забыли это сочетание клавиш, то обратитесь к меню управления окнами Window, найдите строку User Screen с уже упомянутыми клавишами. Кстати, ниже этой строки будет приведён список всех открытых вами окон TD, а именно:

Module Hello (1)

CPU               (2)

Inspector        (3)

Вы можете вызвать любое открытое вами окно с помощью нажатия клавиш <Alt+ номер окна> или путём их последовательного перебора с помощью клавиши <F6-Next>. Если вы хотите закрепить данное расположение окон при следующей работе с TD, то войдите в меню Options (параметры), выберите кнопку с командой Layot (схема окон). Данная схема будет зафиксирована утилитой Tdconfig.dt. Раз уж мы находимся в меню Options, укажем здесь ещё на одну полезную команду Display options (вывести параметры). Всплывающая заставка Display swapping (переключение экрана) позволяет выбрать один из трёх способов управления переключением между экраном TD и экраном пользователя. По умолчанию устанавливается параметр Smart (эффективный), который позволяет переключиться на экран пользователя User Screen автоматически по требованию программы (это мы наблюдали только что при выполнении требования программы Hello операции ввода одиночного символа).

Вернёмся снова в окно CPU для завершения работы с программой Hello. Используя клавишу <F8>, доводим маркер исполнения текущей команды до команды с меткой Exit (вызов функции DOS с номером 4Сh для выхода из программы) и, нажав <Alt+F5>, увидим на экране пользователя ответ программы на наш диалог с ней. Если вы хотите повторить выполнение программы, то она может быть перезагружена с произвольной команды с помощью нажатия клавиш <Ctrl+F2> или командой меню Run = Program Reset (сброс программы). При этом программа снова загружается с диска и TD восстанавливает свои исходные опции. Если вы находитесь в окне CPU или Module, то на дисплее не будет показан возврат к началу вашей программы – для этого надо нажать <F8>.

Завершая краткое описание работы с отладчиком TD, перечислим здесь возможные режимы исполнения программы в TD.

·     Режим автоматического исполнения, клавиша F9 (Run).

·     Выполнение по шагам (клавиши F8 или F7-Trace into). Отличие в назначении этих клавиш проявляется в том, что клавиша F7 используется для пошагового исполнения тела цикла, процедуры или подпрограммы обработки прерывания. Клавиша же F8 исполняет эти процедуры как одну обычную команду и передаёт управление следующей команде программы.

·     Выполнение до текущего положения курсора. Для активизации этого режима необходимо установить курсор на нужную строку программы (строка будет подсвечиваться другим цветом) и нажать клавишу F4.

·     Выполнение с установленными точками прерывания (Breakpoints). Перед исполнением программы необходимо установить эти точки, для чего следует перейти в нужную строку программы и нажать клавишу F2 (toggle). Выбранная строка с контрольной точкой подсвечивается красным цветом. Чтобы убрать контрольную точку, надо повторить эту операцию снова. После установки точек прерывания программа запускается на исполнение клавишей F9. После первого нажатия клавиши F9 программа остановится на первой точке прерывания, после второго – на второй точке и.т.д. Это очень удобный режим отладки программы, когда необходимо контролировать правильность её исполнения в некоторых характерных точках.

Завершать работу с отладчиком следует командой File = Quit или с помощью клавиш <Alt+X>. TD имеет множество других возможностей, изучить которые вам предлагается самостоятельно при исследовании демонстрационных программ, приведённых в приложении П.1.2 к данной работе.

6.2.6. ФОРМАТЫ МАШИННЫХ КОМАНД И ИХ КОДИРОВАНИЕ

Общий формат команд процессора (начиная с модели i80386) приведён на рис 1.5 [6, 8, 13]. Команды состоят из необязательных префиксов, одного или двух байт кода операции, спецификаторов адреса (постбайт и байт sib), внутрикомандного смещения (Displacement) и непосредственных данных. Английские аббревиатуры означают следующее: Base – база, MoDe – режим, Register/Memory – регистр/память,Scale – масштаб, Index – индекс. Из всех полей команды обязательным является только один или два байта кода операции.

Префикс – это байт, который модифицирует операцию следующей за ним команды. Два префикса замены операнда и размера адреса (коды 66h и 67h соответственно) позволяют в реальном режиме изменить принимаемые по умолчанию 16-битные размеры операнда и эффективного адреса ЕА (Effective address) на 32-битные. Здесь необходимо заметить, что при ассемблировании машинной команды Tasmсамостоятельно вставляет соответствующий префикс, если обнаруживает в мнемокоде команды 32-битный операнд или 32-битную ссылку на память.

 

 
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 1.5. Общий формат команды процессора старших моделей

 

В реальном режиме может быть использован как 16-битный режим адресации памяти (единственно возможный в базовом процессоре i8086), так и 32-битный. Однако, учитывая, что в реальном режиме размер любого сегмента ограничен величиной 216 = 64 Кбайта, значение адреса в 32-битном слове не должно превышать величины FFFFh.

6.2.6.1. 16-БИТНЫЙ РЕЖИМ АДРЕСАЦИИ ОПЕРАНДОВ В ПАМЯТИ

Для практического изучения команд процессора i8086 составлен ряд демонстрационных программ (Mov.asm, Arithmet.asm, Logiecal.asm, LoopCall.asm), а в табл. П.1.1_1 приложения П.1.1. приведена система команд, упорядоченная по мнемокоду, с машинными кодами и содержанием выполняемой операции для каждой ассемблерной команды.

По функциональному признаку система команд разбивается на 6 групп: пересылка данных, арифметические операции, логические операции и сдвиги, передача управления, обработка цепочек и управления микропроцессором. В этом параграфе рассмотрим формирование машинных кодов команд процессора, которые могут адресовать один или два операнда и структурно принадлежат к одному из трёх типовых форматов, приведённых на рис.1.6.

Машинные форматы команд в зависимости от типа команды и способа адресации содержат от 1 до 6 байтов, из которых ключевыми являются первый (код операции) и второй (способ адресации). Штриховыми линиями на рис. 1.6 показаны необязательные байты команд. Принципы кодирования полей отдельных байтов команд рассмотрим на примере наиболее общей формы двухоперандной команды.

 

 

 

 
 
 

 

 

 

 

 

 

 

 

Рис. 1.6. Типичные форматы команд:
двухоперандные команды (а) и (б) и однооперандная (в)

 

·     w (Word) – однобитовое поле, идентифицирующее тип операнда: при w = 1 команда оперирует словами, а при = 0 – байтом;

·     d (Direction) – однобитовое поле, определяющее направление передачи операнда или результата. При = 1 осуществляется передача операнда в регистр, определённый полем reg во втором байте, при = 0 – передача из указанного регистра;

·     reg (Register) – 3-битовое поле регистра, условно определяющее назначение второго операнда;

·     r/m (Register/Memory) – 3-битовое поле операнда, который может находиться в памяти или в регистре и условно считается первым;

·     md (Mode) – 2-битовое поле, показывающее (табл. 1.2), как интерпретировать поле r/m при нахождении эффективного адреса EA первого операнда: если md = 11, то операнд содержится в регистре, в остальных случаях – в памяти.

Таблица 1.2

Определение эффективного адреса

 

Поле md

Поле r/m

11

00

01

10

 

W=0

W=1

 

 

 

000

AL

AX

BX+SI

BX+SI+D8

BX+SI+D16

001

CL

CX

BX+DI

BX+DI+D8

BX+DI+D16

010

DL

DX

BP+SI

BP+SI+D8

BP+SI+D16

011

BL

BX

BP+DI

BP+DI+D8

BP+DI+D16

100

AH

SP

SI

SI+D8

SI+D16

101

CH

BP

DI

DI+D8

DI+D16

110

DH

SI

D16)*

BP+D8

BP+D16

111

BH

DI

BX

BX+D8

BX+D16

 

 

Когда операнд находится в памяти, то поле md определяет вариант использования внутрикомандного смещения disp (Displacement), находящегося в 3-м и в 4-м байтах:

md=00,         disp=0;

md=01,         disp =disp L-знаковое 8-битовое смещение (D8), расширяющееся со знаком до 16 бит при вычислении эффективного адреса ЕА;

md=10,         disp =disp H, disp L- знаковое 16-битовое смещение (D16).

При этом кодировка поля reg второго операнда или поля r/m первого операнда при md = 11 определяет тип регистра в зависимости от значения параметра w. При md ¹ 11 реализуются косвенные виды адресации памяти, что и отражено в табл. 1.3 (исключение составляет случай прямой адресации при 
r/m = 110 и md = 00). Таким образом, в процессоре i8086 реализовано 
24 варианта (8*3) вычисления эффективного адреса ЕА.

Таблица 1.3

Косвенные виды адресации

Тип адресации

Способ вычисления EA

Число тактов

Косвенная

Базовая, или индексная

Базовая индексная без смещения

 

 

Базовая индексная со смещением

 

[BX], [BP], [SI], [DI]

[BX+ , BP+ , SI+ , DI+disp]

[BP+DI], [BX+SI]

[BP+SI], [BX+DI]

[BP+DI+disp]

[BX+SI+disp]

[BP+SI+disp]

[BX+DI+disp]

5

6

7

8

11

11

12

13

 

 

Замечание. Подчеркнём смысловое различие двух случаев употребления термина смещение. Смещение disp, содержащееся в команде, интерпретируется как знаковое число, которое участвует в вычислении эффективного адреса EA. С другой стороны, из-за сегментной организации памяти эффективный адрес EA является смещением OFFSET относительно базового адреса сегмента и интерпретируется как беззнаковое число при вычислении физического адреса (см. рис. 2, п. 4).

При вычислении физического адреса эффективный адрес (или смещение Offset) сегментируется регистром ds (сегмент данных) во всех случаях кроме тех, когда в формировании EA участвует сегментный регистр bp. Использование регистра bp в формировании EA предопределяет сегментирование адреса 
по регистру ss (сегмент стека). Чтобы изменить, указанное по умолчанию 
(см. также табл. 3, § 5) сегментирование адресов, предусмотрена специальная однобайтная команда-префикс замены сегмента:

001 Sreg 110 – формат команды префикса замены.

Если команде предшествует префикс замены, то при обращении к данным в процессе исполнения команды участвует сегментный регистр из префикса. Кодировка поля Sreg в префиксе: 00 – es, 01 – cs, 00 –ss, 11 – ds.

На рис. 1.6 б представлен формат двухоперандной команды с непосредственным операндом. В этом формате отсутствует необходимость в адресации второго операнда и поле reg отдано для расширения поля COP. Отсутствует также бит направления d, т.к. результат операции размещается на месте первого операнда. Место бита d занял бит s, функции которого определяются следующим образом:

sw = X0,     data = data L -один байт данных;

sw = 01,      data = dataH,dataL – два байта данных;

sw = 11,      data = dataL – один байт данных, который расширяется со знаком до двух байт.

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

Двоичное кодирование ассемблерных команд, рекомендуется выполнять в следующей последовательности:

–     находим обобщённое представление команды, соответствующее одной из альтернативных записей команды в справочном списке команд (Приложение П. 1.1);

–     для выбранного обобщённого представления команды выписываем из таблицы её формат;

–     принимая во внимание формат данных, способ кодирования регистров и тип адресного выражения для операнда, находящегося в памяти (табл. 1.2) , определяем двоичный код для обозначенных в команде полей;

–     записываем структурированный по байтам машинный код команды с использованием 16-теричных символов. Полученный код должен совпадать с машинным кодом соответствующей команды из окнаCPU Turbo Debugger для рассматриваемого файла.

В качестве примера ниже рассматривается машинная кодировка трёх ассемблерных команд, взятых из файла mov.asm. В качестве источника проверки машинного кода было использовано окно CPU Turbo Debugger.

Dataseg

w_x                                    dw     8,16,32,64                ;Offset w_x = 0004h

Codeseg

               

cs:000C – BB0400             mov bx,offset w_x               ;непосредственная адресация

               

cs:0014 – 8B160400          mov dx,[w_x]                     ;прямая адресация

               

cs:002C – C747021800     mov [word bx+2],24           ;базовая адресация

                

¨  Команда: mov bx,offset w_x. В регистр bx занести offset переменной w_x.

1) Справочные данные команды:

mov r16,d16 ←→ 1011-w-reg                 dataL          (dataH)

2) Кодировка обозначенных полей команды в пп. 1:

– w = 1, так как формат данных типа word;

– операнд-приёмник: reg = code(bx )= 011;

– операнд – источник: data H:dataL = offset w_x = 00h:04h – 16-битный адрес переменной w_x в сегменте данных. Этот адрес может быть легко вычислен путём просмотра списка переменных в памяти данных файла mov.asm.

3) Машинный код, таким образом, равен:        04h  00h = BB  04  00h

¨    Команда: mov dx,[w_x]. В регистр dx занести значение переменной w_x.

1) Справочные данные команды:

mov r16,r16/m16 ←→ 1000101-w          md-reg-r/m (dispL dispH)

2) Кодировка обозначенных полей команды в пп. 1

– w = 1, так как формат данных типа word;

– операнд-приёмник: reg = code(dx)  =010;

– операнд-источник: содержимое ссылки на адрес переменной w_x. Прямая адресация с адресным выражением, обозначенным как D16 {d= 00, r/m = 110} в табл. 1.2. Значение адреса D16 = dispH:dispL= offset w_x = 00h:04h определяется из просмотра списка переменных в памяти данных файла mov.asm.

3) Машинный код, таким образом, равен:

1000101-1           00-010-110                   04h 00h = 8B  16  04  00h

¨  Команда: mov [word bx+2],24. В ячейку памяти по адресу [bx+2], занести константу 24, зарезервировав под неё два байта (word).

1) Справочные данные команды:

mov r16/m16,d16 ↔ 1100011-w              md-000-r/m           (dispL  dispHdataL  dataH

2) Кодировка обозначенных полей команды в пп. 1:

– w = 1, так как формат данных типа word;

– операнд-приёмник: ячейка памяти, определяемая адресным выражением [word dx + 2] = [x+ d8], что определяет поля постбайта как: md = 01, r/m = 111;

– внутрикомандное смещение d8 = dispL = 02h, поле dispH исключается;

– операнд-источник константа 24 = 00 18h, так как формат данных типа word.

3) Машинный код, таким образом, равен:

1100011-1           01-000-111                   02   18        00 = C7  47  02  18  00 h

6.2.6.2. Использование 32-битных регистров

Реальный режим допускает использование 32-битных регистров (eax, ebx, ecx, edx, esi, edi, ebp, esp) как для хранения данных, так и для формирования 32-битных адресных выражений. 32-битная адресация предполагает и другой (более сложный) способ кодирования машинных команд, в котором наряду с постбайтом, участвует спецификатор адреса – байт sib (см. рис. 1.4). Необходимо также указывать спецификатор размера сегмента use16 при использовании стандартных директив управления сегментами, например:

P486n

Segment text      use16

................

Segment date     use16

Ниже указываются основные отличия в 32 -битной адресации.

Первое отличие. В базовой и индексной адресациях может быть использован любой из 32-битных регистров. При этом если в качестве базового выступает один из регистров esp или ebp, то адресация будет осуществляться через сегментный регистр ss, хотя переназначение сегмента возможно. Адресация называется базовой или индексной (со смещением или нет) не по виду используемого регистра, а по способу формирования эффективного адреса ЕА.

 

Пример.

w_table            dw                        1,2,4,8,16,32

……………..

;Базовая адресация

mov eax,offset w_table      

mov bx,[eax + 6]                          ;Загрузка элемента списка w_table c индексом к=3 в bx

;(3 * type w_table=6), т.е. bx ¬ 8.  Здесь регистр eax -элемент адресного выражения и

;соответствия типов регистров bx и eax не требуется. Формат данных типа word и

;определяется регистром bx.

.......................

;Индексная адресация

mov eax,6                                    ;eax ¬ 3* type w_table

mov bx,[w_table + eax]                ;bx ¬ 8. Тип переменной w_table и регистра bx соответст-

                                                    ;вуют друг другу.

 

Второе отличие заключается в возможности масштабирования содержимого индексного регистра (любой, кроме esp), т. е. его умножения на заданный в команде коэффициент: 1, 2, 4 или 8. Тип индексного регистра не влияет на установленный по умолчанию выбор сегментного регистра, относительно которого осуществляется адресация.

Пример базовой индексной адресации с масштабированием:

mov edx,[Array + ecx + eax * 4]

Здесь Array двумерный массив элементов типа dword, ecx – базовый адрес строки, eax – индекс элемента строки.

Поскольку программа может оперировать одновременно с 16-битными и 32-битными регистрами, то для их различия Tasm автоматически добавляет в машинный код программы префикс размера операнда 66h (если идёт "речь" о типе операнда) или префикс размера адреса 67h при использовании расширенных регистров в адресных выражениях (конечно, являются очевидными случаи с наличием обоих префиксов одновременно).

Однако, в некоторых случаях Tasm это не делает [12]. Так, например, перед командой организации цикла Loop, использующей расширенный регистр ecx, это делать приходится самому программисту:

                            Mov ecx,8008000          ;Счётчик цикла

          Delay:         db               67h             ;Префикс размера адреса

                            Loop           delay           ;Повторим команду Loop 800 8000 (можно ≤ 232 раз).

Если это не сделать, то цикл будет выполнен только 8000 раз (можно 
≤ 216 – 1 раз). Другой способ – использование альтернативных форм команды loop: loopw (loopwe, loopwne, ...) при использовании счётчика cx и loopd (loopde, loopdne, ...) при использовании ecx.

В заключение следует сказать, что возможности использования в программах реального режима дополнительных средств 32-разрядных процессоров в действительности ограничены. Новых команд не так уж много и они не носят принципиального характера, а 32-разрядные данные в прикладных "ассемблерных" программах используются относительно редко.

6.2.7. РАБОТА НАД СИНТАКСИЧЕСКИМИ ОШИБКАМИ 
ПРИ АССЕМБЛИРОВАНИИ ПРОГРАММЫ

Даже опытные программисты делают синтаксические ошибки при написании программы. Работа над допущенными в программе ошибками, также как и её написание, требует предварительного знакомства со следующими вопросами, которые выходят из рамок настоящего пособия:

–      запись ассемблерной строки с мнемокодом команды или директивой;

–      пользовательские и служебные идентификаторы. Имена, метки, переменные;

–      форматы данных и директивы их инициализации в памяти (dbdwdd,...).

–      макроопределения символьных констант, выражений и строк (директивы = и  equ);

–      константные и адресные выражения;

–      общее знакомство с командами процессоров i80x86.

Здесь ограничимся указанием перечня наиболее часто допускаемых ошибок при написании первых программ, наличие которых (с указанием номера строки) будет отмечено Tasm при ассемблировании:

·     искажение имени команды или директивы;

·     опущено двоеточие ':' после имени метки;

·     неправильная запись пользовательского идентификатора;

·     в качестве оператора содержится неопределённый ранее идентификатор;

·     использование имени служебного идентификатора в качестве пользовательского;

·     значение инициализируемой константы превышает допустимую величину, например:

db          400      ;400 > 255, т. е. максимального значения в формате byte

·     наличие запятой в конце списка элементов при множественной инициализации, например:

Dw         1,3,5,400,        ;лишняя запятая в конце списка

·     отсутствие круглых скобок в операторе dup, например:

Db         4 dup 7            ;надо "4 dup (7)"

·     ошибка в написании имени модели памяти или её отсутствие при использовании упрощенных директив;

·     опущена одна или обе квадратные скобки [], заключающие адресное выражение в режиме Ideal, например:

Mov ax, [bx+si               ;содержимое по адресу bx+si отправить в ax

·     не совпадают типы операндов, с которыми оперирует команда, например:

Mov al,[var_w]         ;ошибка, – ранее переменная var_w была определена в формате word

Mov ax,[var_b]         ;ошибка, – ранее переменная var_b была определена в формате byte

·     требуется явно указать тип операнда, например:

Mov [bx],1                         ;ошибка, – надо явно указать тип ячейки памяти: [byte bx] или

                                           ;[word bx] и. т. п.

·     операнд в текущей инструкции не может быть ссылкой на адрес памяти:

Mov [oper_1],[oper_2]      ;ошибка, – инструкция mov не может передавать данные из

                                           ;одной ячейки памяти в другую

·     недопустимый режим адресации, например:

Add [dx+si],ax          ;ошибка, – регистр dx не может быть использован в косвенной

                                 ;адресации

·     недопустимая команда для выбранного в настоящий момент типа процессора. По умолчанию используется i8086;

·     адрес назначения в команде условного перехода находится вне допустимого для процессора i8086 диапазона, т. е. не принадлежит интервалу (– 128, 
+ 127);

·     непарное использование директивы endp, например:

Proc            addition

...........

endp           subtraction            ; ошибка, -должно быть указано имя процедуры addition

Аналогичные ошибки могут быть допущены и при использовании других парных директив "segment <name> ... ends <name>", "macro <name> .... endm <name>" и. т. п.

·     в конце программы отсутствует директива End.

6.3. ЗАДАНИЕ К РАБОТЕ. ПОРЯДОК ВЫПОЛНЕНИЯ

1. Произвести ассемблирование и компоновку программы hello.asm с опциями, предполагающими использование отладчика TD. Повторить программу исследования файла hello.exe в режиме TD, следуя рекомендациям параграфа п. 6.2.4 лабораторной работы.

2. В соответствии с вариантом задания (табл. 1.4) произвести ассемблирование и компоновку программных файлов, с опциями, предполагающими получение листинга и отладочной информации для TD. Для обоих файлов составить диаграмму размещения сегментов в памяти по образцу рис. 1.7. Информация о расположении сегментов любой из программ должна согласовываться также с данными таблицы идентификаторов, расположенной в конце листинга ассемблирования, или с данными файла отображения программы в памяти hello.map.

 

 
 
 

 

 

 

 

 

 

 

 

 

 

Рис. 1.7. Диаграмма расположения сегментов программы hello.exe в памяти

 

 

3. Работа над собственной программой предстоит на следующем практическом занятии, а на этом полезно будет выяснить, какую помощь в этом плане вы можете ожидать от асссемблера. Создайте копии файлов программ п.п. 2 c видоизменёнными названиями, с целью проведения эксперимента по их ассемблированию (получение листинга: tasm/l <file.asm>) с введёнными синтаксическими ошибками (достаточно 5-7). При введении ошибок, в синтаксически правильно составленные программы, можно руководствоваться содержанием п. 6.2.6.

Ошибки вместе с замечаниями на них Turbo Assembler выводит в листинге. Более подробную трактовку о сделанной ошибке можно узнать, обратившись к файлу ошибок ассемблирования Tasm_err.doc [6].

Отчёт по лабораторной работе должен содержать:

–     диаграмму расположения сегментов в заданных для исследования программах;

–     работу над синтаксическими ошибками, проведённую для тех же программ;

–     анализ машинных кодов ассемблерных команд с указанием используемых видов адресации. В анализе необходимо показать непротиворечивость кода, сформированного транслятором, изложенным выше принципам кодирования. Номера команд из сегмента Codeseg, подлежащих анализу, приведены в табл. 1.4. Указанные номера команд идентифицированы цифрами в поле "комментарий" соответствующих файлов. Каждый вариант задания включает анализ 6 команд из двух файлов.

Таблица 1.4

Таблица индивидуальных заданий к работе № 1

Файл

Вариант

 

 

 

1, 7

2, 8

3, 9

4, 10

5, 11

6, 12

7, 14

 

Mov.asm

4, 5, 6

7, 13, 14

15, 16, 18

 

15, 16, 18

21, 22, 23

15, 16, 18

 

Arithmet.asm

 

6, 9, 13

 

9, 16, 17

 

17,19, 20

 

 

Logical.asm

 

 

6, 7, 10

 

12, 15, 21

 

 

 

LoopCall.asm

6, 9, 18

 

 

6, 9, 18

 

 

6, 9, 18

                     
 

6.4. КОНТРОЛЬНЫЕ ВОПРОСЫ

1.       Запишите команду для ассемблирования файла f_1.asm с учётом формирования листинга и возможности использования отладчика TD.

2.       Запишите команду для компоновки файла f_1.obj, разрешающую использование отладчика и запрещающего создание файла отображения.

3.       Как определить физический объект в сегментированной модели памяти, если известны его логические компоненты: "Segment – Offset"?

4.       Запишите директивы языка ассемблера, управляющие сегментами стека, данных и кода в режиме Ideal (Masm).

5.       В чём состоит принципиальная разница между исполняемыми файлами программ типа .exe и .com

6.       Запишите команды, определяющие начало и конец программного кода.

7.       В чём состоит различие между ошибками и предупреждениями, создаваемыми Tasm во время трансляции? Ваши действия при обнаружении ошибки.

8.       После загрузки программы в TD требуется инициализировать сегментный регистр ds на начало сегмента данных. Изложите последовательность ваших действий.

9.       Опишите основные форматы команд 16-разрядных процессоров.

10.   Изложите состав и назначение полей постбайта (байта адресации) в двухоперандной команде процессора.

11.   Укажите на основные отличия в механизме адресации 16- и 32-разрядных процессоров.

12.   Какие из приведённых ниже команд записаны с ошибками и почему? Считайте все идентификаторы переменными, которые определены как слова в сегменте данных:

 

mov bp,al

mov [op_1+bx+di+12],ax

mov [op_1],[op_2]

mov ax,[op_3+dx]

mov cs,ax

mov [bx+si],2

lea bx,op_2

mov bx,offset [op_2]

 

 

[1] Файл Autoexec.nt в системе Windows 2000 можно использовать для указания путей поиска (как и указывалось выше) только для программ DOS или с ней совместимых. Программы FAR и Windows Commander к таким не относятся, поэтому при использовании командных строк данных программ для работы с DOS-программами, требуется прописать соответствующие пути поиска в окне Переменные среды для пользователя. Наиболее просто открыть это окно можно следующим образом. Выделим на зкране монитора значок Мой компьютер с последующим вызовом диалогового меню. Выполним последоватльно команды: Свойства/Дополнительно/Переменные среды. Далее в окне Переменные среды для пользователя нажимаем кнопку Создать и, в диалоговом окне Новая пользовательская переменная, прописываем c помощью команды Path нахождение каталогов с програм-
мами – оболочками и пакетом Tasm5.

[2] Некоторая информация представленная на рис. 1.2, будет понятна из рассмотрения материала п. 6.2.5.