Существует два основных типа загрузочных программ: EXE и COM. Рассмотрим требования к EXE-программам. DOS имеет четыре требования для инициализации ассемблерной EXE-программы: 1) указать ассемблеру, какие сегментные регистры должны соответствовать сегментам, 2) сохранить в стеке адрес, находящийся в регистре DS, когда программа начнет выполнение, 3) записать в стек нулевой адрес и 4) загрузить в регистр DS адрес сегмента данных.
Существуют определенные различия между программой, выполняемой как EXE-файл и программой, выполняемой как COM-файл.
1. Размер программы. EXE-программа может иметь любой размер, в то время как COM-файл ограничен размером одного сегмента и не превышает 64К. COM-файл всегда меньше, чем соответствующий EXE-файл; одна из причин этого - отсутствие в COM-файле 512-байтового начального блока EXE-файла.
2. Сегмент стека. В EXE-программе определяется сегмент стека, в то время как COM-программа генерирует стек автоматически. Таким образом при создании ассемблерной программы, которая будет преобразована в COM-файл, стек должен быть опущен.
|
|
3. Сегмент данных. В EXE программе обычно определяется сегмент данных, а регистр DS инициализируется адресом этого сегмента. В COM-программе все данные должны быть определены в сегменте кода.
4. Инициализация. EXE-программа записывает нулевое слово в стек и инициализирует регистр DS. Так как COM-программа не имеет ни стека, ни сегмента данных, то эти шаги отсутствуют. Когда COM-программа начинает работать, все сегментные регистры содержат адрес префикса программного сегмента (PSP),
- 256-байтового (шест. 100) блока, который резервируется операционной системой DOS непосредственно перед COM или EXE программой в памяти. Так как адресация начинается с шест. смещения 100 от начала PSP, то в программе после оператора SEGMENT кодируется директива ORG 100H.
5. Обработка. Для программ в EXE и COM форматах выполняется ассемблирование для получения OBJ-файла, и компоновка для получения EXE-файла. Если программа создается для выполнения как EXE-файл, то ее уже можно выполнить. Если же программа создается для выполнения как COM-файл, то компоновщиком будет выдано сообщение:
Warning: No STACK Segment
(Предупреждение: Сегмент стека не определен)
ПРИМЕР ПРОГРАММЫ
Программа преобразования двузначного шестнадцатеричного числа в символьном виде в двоичное представление.
Вход: исходное шестнадцатеричное число из двух цифр вводится с клавиатуры
Выход: результат преобразования помещается в регистр dl.
<1> data segment para public “data”;сегмент данных
<2> message db “Введите две 16-теричные цифры, $”
<3> data ends
<4> stk segment stack
<5> db 256 dup (“?”);сегмент стека
<6> stk ends
|
|
<7> code segment para public “code”;начало сегмента кода
<8> main proc;начало процедуры main
<9> assume cs:code,ds:data,ss:stk
<10> mov ax,data;адрес сегмента данных в регистр ах
<11> mov ds,ax;ax в ds
<12> mov ah,9
<13> mov dx,offset message
<14> int 21h
<15> xor ax,ax;очистить регистр ах
<16> mov ah,1h;1h в регистр ah
<17> int 21h;генерация прерывания с номером 21h
<18> mov dl,al;содержимое регистра al в регистр dl
<19> sub dl,30h;вычитание: (dl)=(dl)-30h
<20> cmp dl,9h;сравнить (dl) с 9h
<21> jle M1;перейти на метку М1, если dl<9h или dl=9h
<22> sub dl,7h;вычитание: (dl)=(dl)-7h
<23> M1:;определение метки М1
<24> mov cl,4h;пересылка 4h в регистр сl
<25> shl dl,cl;сдвиг содержимого dl на 4 разряда влево
<26> int 21h;вызов прерывания с номером 21h
<27> sub al,30h;вычитание: (dl)=(dl)-30h
<28> cmp al,9h;сравнить (al) c 9h
<29> jle M2;перейти на метку М2, если al<9h или al=9h
<30> sub al,7h;вычитание: (al)=(al)-7h
<31> M2:;определение метки М2
<32> add dl,al;сложение: (dl)=(dl)+(al)
<33> mov ax,4c00h;пересылка 4с00h в регистр ax
<34> int 21h;вызов прерывания с номером 21h
<35> main endp;конец процедуры main
<36> code ends;конец сегмента кода
<37> end main;конец программы с точкой входа main
Строки 1-3 определяют сегмент данных. В строке 2 описана текстовая строка с сообщением «Введите две шестнадцатеричные цифры».
Строки 4-6 описывают сегмент стека, который является просто областью памяти длиной 256 байт, инициализированной символами «”?”». Отличие сегмента стека от сегментов других типов состоит в использовании и адресации памяти. В отличие от сегмента данных (наличие которого необязательно, если программа не работает с данными), сегмент стека желательно определять всегда.
Строки 7-36 содержат сегмент кода. В этом сегменте в строках 8-35 определена одна процедура main.
Строка 9 содержит директиву ассемблера, которая связывает сегментные регистры с именами сегментов.
Строки 10-11 выполняют инициализацию сегментного регистра DS.
Строки 12-14 выводят на экран сообщение message:
Строка 15 подготавливает регистр АХ к работе, обнуляя его.
Строки 16-17 обращаются к средствам операционной системы для ввода символа с клавиатуры. Введенный символ операционная система помещает в регистр AL.
Строка 18 пересылает содержимое AL в регистр DL. Это делается для того, чтобы освободить AL для ввода второй цифры.
Строка 19 преобразует символьную цифру в ее двоичный эквивалент путем вычитания 30h, в результате чего в регистре DL будет двоичное значения числа.
В строках 20-21 выясняется, нужно ли корректировать двоичное значение DL. Если оно лежит в диапазоне 0…9, то в DL находится правильный двоичный эквивалент введенного символа шестнадцатеричной цифры. Если значение в DL больше 9, то введенная цифра является одним из символов A,B,C,D,E,F. В первом случае строка 21 передаст управление на метку М1.
В строках 24-25 значение в DL сдвигается на 4 разряда влево, освобождая место в младшей тетраде под младшую шестнадцатеричную цифру.
В строке 26 в регистр AL вводится вторая шестнадцатеричная цифра.
В строках 27-29 выясняется, попадает ли двоичный эквивалент второго символа шестнадцатеричной цифры в диапазон 0…9. Наша вторая цифра не попадает в диапазон, поэтому для получения правильного двоичного эквивалента нужно произвести дополнительную корректировку. Это делается в строке 38.
Строки 33-34 предназначены для завершения программы и возврата управления операционной системе.
СТРУКТУРЫ
Описание типа структуры
Структура - это составной объект, занимающий несколько соседних ячеек памяти. Это тип данных, состоящий из фиксированного числа элементов разного типа. Компоненты структуры называются полями, они могут быть разного типа (размера) - байт, слово и т. д. Поля именуются, доступ к полям осуществляется по именам. Для использования структур в программе необходимо выполнить 3 действия.
|
|
1. Задать шаблон структуры. По смыслу это означает определение нового типа данных, который впоследствии можно использовать для определения переменных этого типа.
2. Определить экземпляр структуры. Этот этап подразумевает инициализацию конкретной переменной с заранее определенной структурой.
3. Организовать обращение к элементам структуры.
Существует разница между описанием структуры в программе и ее определением. Описание структуры в программе означает лишь указание компилятору ее схемы или шаблона; память при этом не выделяется. Компилятор извлекает из этого шаблона информацию о расположении полей структуры и их значениях по умолчанию. Определение структуры означает указание транслятору на выделение памяти и присвоение этой области памяти символического имени. Описать структуру можно только один раз, а определить любое количество раз.
Описание шаблона структуры.
Прежде чем использовать структуру, надо описать ее тип - указать, сколько в ней полей, какие имена у полей и т. д. Описание типа структуры выглядит так:
<имя типа> STRUC
<описание поля>
…….
<описание поля>
<имя типа> ENDS
Описание типа открывает директива STRUC, где указывается имя, которое дали типу структуры. Это же имя должно быть в директиве ENDS. Между этими двумя директивами может быть указано любое число директив, описывающих поля структуры.
Например, тип структуры DATE из трех полей Y (год), M(месяц), D(день) можно описать так:
DATE STRUC
Y DW 2004
M DB 3
D DB?
DATE ENDS
Описание типа структуры носит чисто информационный характер, по нему ассемблер ничего не заносит в машинную программу, поэтому такое описание можно размещать в любом месте программы, но обязательно до описания переменных этого типа.
После того как описан тип структуры, можно определять в программе переменные этого типа, отводить под них память. Такие переменные называются переменными-структурами или просто структурами. Описываются они с помощью директив следующего вида:
имя_переменной имя_типа <нач_знач {, нач_знач}>
|
|
Пример описания переменных-структур:
DT1 DATE <?, 6, 9>
DT2 DATE <2005,, >
DT3 DATE <,, >
Y | M | D |
? | ||
? | ||
? |
Это директивы особого рода. До этого мы рассматривали директивы, названиями которых были служебные слова, а здесь названием является имя DATE. Каждая такая директива описывает одну переменную, имя которой указывается в начале директивы.
DT2 DATE <2005,, > эквивалентно DT2 DATE <2005>
DT3 DATE <,, > эквивалентно DT3 DATE < >
Одной директивой можно описать сразу несколько структур, т. е. можно описать массив, элементами которого являются структуры. Например по директиве
DTS DATE <, 12, 5 > 10 dup (<>)
описывается массив из 11 структур типа DATE, причем поля первой из них будут иметь начальные значения: 2004, 12, 5, а остальные десять структур получают один и тот же набор начальных значений, взятых по умолчанию: 2004, 3,?.
Описав тип структуры и переменные этого типа, получаем право работать с этими переменными-структурами. Как единое целое структуры обрабатываются редко, обычно они обрабатываются по полям. чтобы сослаться на поле структуры, надо использовать конструкцию вида
<имя переменной-структуры>.<имя поля>
DT3.D
Такая конструкция обозначает ту ячейку памяти, которую занимает указанное поле указанной переменной. Встречая эту конструкцию, ассемблер заменяет ее на адрес данной ячейки.
Пример. Если в переменной DT1 хранится мартовская дата, то требуется записать сюда дату следующего дня года.
cmp DT1.M, 3
jne FIN; не март - FIN
cmp DT1.M, 31
je APRL; 31 марта - APRL
inc DT1.D; следующий день в марте
jmp FIN
APRL: mov DT1.M, 4; замена 31 марта 1 апреля
mov DT1.D, 1
FIN: ….
ОБЪЕДИНЕНИЯ
Объединение - тип данных, позволяющий трактовать одну и ту же область памяти как данные, имеющие разные типы и имена. Описание объединений в программе напоминает описание структур, т. е. сначала указывается шаблон, в котором с помощью директив описания данных перечисляются имена и типы полей:
имя_объединения UNION
<описание полей>
имя_объединения ENDS
Отличие объединений от структур состоит в том, что при определении переменной типа объединения память выделяется в соответствии с размером максимального элемента. Обращение к элементам объединения происходит по их именам, но при этом нужно помнить, что все поля в объединении накладываются друг на друга. В качестве элементов объединения можно использовать и структуры.
ПРОЦЕДУРЫ