Жанр: Учеба
Ассемблер и программирование для IBM PC
...одного сегмента и не
превышает 64К. COM-файл всегда меньше, чем соответствующий EXE-файл; одна
из причин этого - отсутствие в COM-файле 512-байтового начального блока
EXE-файла.
С е г м е н т с т е к а. В EXE-программе определяется сегмент стека,
в то время как COM-программа генерирует стек автоматически. Таким образом
при создании ассемблерной программы, которая будет преобразована в
COM-файл, стек должен быть опущен.
С е г м е н т д а н н ы х. В EXE программе обычно определяется
сегмент данных, а регистр DS инициализируется адресом этого сегмента. В
COM-программе все данные должны быть определены в сегменте кода. Ниже
будет показан простой способ решения этого вопроса.
И н и ц и а л и з а ц и я. EXE-программа записывает нулевое слово в
стек и инициализирует регистр DS. Так как COM-программа не имеет ни стека,
ни сегмента данных, то эти шаги отсутствуют. Когда COM-программа начинает
работать, все сегментные регистры содержат адрес префикса программного
сегмента (PSP), - 256-байтового (шест. 100) блока, который резервируется
операционной системой DOS непосредственно перед COM или EXE программой в
памяти. Так как адресация начинается с шест. смещения 100 от начала PSP,
то в программе после оператора SEGMENT кодируется директива ORG 100H.
О б р а б о т к а. Для программ в EXE и COM форматах выполняется
ассемблирование для получения OBJ-файла, и компановка для получения
EXE-файла. Если программа создается для выполнения как EXE-файл, то ее уже
можно выполнить. Если же программа создается для выполнения как COM-файл,
то компановщиком будет выдано сообщение:
Warning: No STACK Segment
(Предупреждение: сегмент стека не определен)
Это сообщение можно игнорировать, так как определение стека в
программе не предполагалось. Для преобразования EXE-файла в COM-файл
используется программа EXE2BIN. Предположим, что EXE2BIN имеется на
дисководе A, а скомпонованный файл по имени CALC.EXE - на дисководе B.
Введите
EXE2BIN B:CALC,B:CALC.COM
Так как первый операнд всегда предполагает EXE файл, то можно не
кодировать тип EXE. Второй операнд может иметь другое имя (не CALC.COM).
Если не указывать тип COM, то EXE2BIN примет по умолчанию тип BIN, который
впоследствии можно переименовать в COM. После того как преобразование
будет выполнено можно удалить OBJ- и EXE-файлы.
Если исходная программа написана для EXE-формата, то можно, используя
редактор, заменить команды в исходном тексте для COM файла.
ПРИМЕР COM-ПРОГРАММЫ
________________________________________________________________
Программа EXCOM1, приведенная на рис.6.1, аналогична программе на
рис.4.3, но изменена согласно требований COM-формата. Обратите внимание на
следующие изменения в этой COM-программе:
- Стек и сегмент данных отсутствует.
- Оператор ASSUME указывает ассемблеру установить относительные
адреса с начала сегмента кодов. Регистр CS также содержит этот адрес,
являющийся к тому же адресом префикса программного сегмента (PSP).
Директива ORG служит для резервирования 100 (шест.) байт от
начального адреса под PSP.
- Директива ORG 100H устанавливает относительный адрес для
начала выполнения программы. Программный загрузчик использует этот
адрес для командного указателя.
- Команда JMP используется для обхода данных, определенных в
программе.
Ниже показаны шаги для обработки и выполнения этой программы:
MASM [ответы на запросы обычные]
LINK [ответы на запросы обычные]
EXE2BIN B:EXCOM1,B:EXCOM1.COM
DEL B:EXCOM1.OBJ,B:EXCOM1.EXE (удаление OBJ и EXE-файлов)
Размеры EXE- и COM-программ - 788 и 20 байт соответственно. Учитывая
такую эффективность COM-файлов, рекомендуется все небольшие программы
создавать для COM-формата. Для трассировки выполнения программы от начала
(но не включая) команды RET введите DEBUG B:EXCOM1.COM.
Некоторые программисты кодируют элементы данных после команд так, что
первая команда JMP не требуется. Кодирование элементов данных перед
командами позволяет ускорить процесс ассемблирования и является методикой,
рекомендуемой в руководстве по ассемблеру.
__________________________________________________________________________
page 60,132
TITLE XCOM1 COM-программа для пересылки и сложения
CODESG SEGMENT PARA 'Code'
ASSUME CS:CODESG,DS:CODESG,SS:CODESG,ES:CODESG
ORG 100H ;Начало в конце PSP
BEGIN: JMP MAIN ;Обход через данные
; ---------------------------------------------------
FLDA DW 250 ;Определение данных
FLDB DW 125
FLDC DW ?
; ---------------------------------------------------
MAIN PROC NEAR
MOV AX,FLDA ;Переслать 0250 в AX
ADD AX,FLDB ;Прибавить 0125 к AX
MOV FLDC,AX ;Записать сумму в FLDC
RET ;Вернуться в DOS
MAIN ENDP
CODESG ENDS
END BEGIN
__________________________________________________________________________
Рис.6.1. Пример COM-программы.
СТЕК ДЛЯ COM-ПРОГРАММЫ
________________________________________________________________
Для COM-файла DOS автоматически определяет стек и устанавливает
одинаковый общий сегментный адрес во всех четырех сегментных регистрах.
Если для программы размер сегмента в 64К является достаточным, то DOS
устанавливает в регистре SP адрес конца сегмента - шест.FFFE. Это будет
верх стека. Если 64К байтовый сегмент не имеет достаточно места для стека,
то DOS устанавливает стек в конце памяти. В обоих случаях DOS записывает
затем в стек нулевое слово.
Возможность использования стека зависит от размера программы и
ограниченности памяти. С помощью команды DIR можно определить размер файла
и вычислить необходимое пространство для стека.
Все небольшие программы в этой книге в основном расчитаны на
COM-формат.
ОСОБЕННОСТЬ ОТЛАДКИ
________________________________________________________________
Несоблюдение хотя бы одного требования COM-формата может послужить
причиной неправильной работы программы. Если EXE2BIN обнаруживает ошибку,
то выдается сообщение о невозможности преобразования файла без указания
конкретной причины. Необходимо проверить в этом случае директивы SEGMENT,
ASSUME и END. Если опущен ORG 100H, то на данные в префиксе программного
сегмента будут установлены неправильные ссылки с непредсказуемым
результатом при выполнении.
При выполнении COM-программы под управлением отладчика DEBUG
необходимо использовать команду D CS:100 для просмотра данных и команд. Не
следует выполнять в отладчике команду RET; предпочтительнее использовать
команду Q отладчика. Некоторые программисты используют INT 20H вместо
команды RET.
Попытка выполнить EXE-модуль программы, написанной для COM-формата,
не имеет успеха.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ
________________________________________________________________
- Объем COM-файла ограничен 64К.
- COM-файл меньше, чем соответствующий EXE-файл.
- Программа, написанная для выполнения в COM-формате не содержит
стека и сегмента данных и не требует инициализации регистра DS.
- Программа, написанная для выполнения в COM-формате использует
директиву ORG 100H после директивы SEGMENT для выполнения с адреса после
префикса программного сегмента.
- Программа EXE2BIN преобразует EXE-файл в COM-файл, обусловленный
указанием типа COM во втором операнде.
- Операционная система DOS определяет стек для COM-программы или в
конце программы, если позволяет размер, или в конце памяти.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ
________________________________________________________________
6.1. Каков максимальный размер COM-файла?
6.2. Какие сегменты можно определить в программе, которая будет
преобразована в COM-файл?
6.3. Как обходится COM-файл при выполнении с фактом отсутствия
определения стека?
6.4. Программа в результате компановки получала имя SAMPLE.EXE.
Напишите команду DOS для преобразования ее в COM-файл.
6.5. Измените программу из вопроса 4.6 для COM-формата, обработайте
ее и выполните под управлением отладчика DEBUG.
ГЛАВА 7 Логика и Организация Программы
__________________________________________________________________________
Ц е л ь: Раскрыть механизм передачи управления в программе (циклы и
переходы) для логических сравнений и программной организации.
ВВЕДЕНИЕ
________________________________________________________________
До этой главы примеры выполнялись последовательно команда за
командой. Однако, программируемые задачи редко бывают так просты.
Большинство программ содержат ряд циклов, в которых несколько команд
повторяются до достижения определенного требования, и различные проверки,
определяющие, какие из нескольких действий следует выполнять. Обычным
требованием является проверка - должна ли программа завершить выполнение.
Эти требования включают передачу управления по адресу команды,
которая не находится непосредственно за выполняемой в текущий момент
командой. Передача управления может осуществляться вперед для выполнения
новой группы команд или назад для повторения уже выполненных команд.
Некоторые команды могут передавать управление, изменяя нормальную
последовательность шагов непосредственной модификацией значения смещения в
командном указателе. Ниже приведены четыре способа передачи управления
(все будут рассмотрены в этой главе):
Безусловный переход: JMP
Цикл: LOOP
Условный переход: Jnnn (больше,меньше,равно)
Вызов процедуры: CALL
Заметим, что имеется три типа адресов: SHORT, NEAR и FAR. Адресация
SHORT используется при циклах, условных переходах и некоторых безусловных
переходах. Адресация NEAR и FAR используется для вызовов процедур (CALL) и
безусловных переходов, которые не квалифицируются , как SHORT. Все три
типа передачи управления воздействуют на содержимое регистра IP; тип FAR
также изменяет регистр CS.
КОМАНДА JMP
________________________________________________________________
Одной из команд обычно используемых для передачи управления является
команда JMP. Эта команда выполняет безусловный переход, т.е. обеспечивает
передачу управления при любых обстоятельствах.
В COM-программе на рис. 7.1 используется команда JMP. В регистры AX,
BX, и CX загружается значение 1, и затем в цикле выполняются следующие
операции:
прибавить 1 к регистру AX,
прибавить AX к BX,
удвоить значение в регистре CX.
Повторение цикла приводит к увеличению содержимого регистра AX:
1,2,3,4..., регистра BX: 1,3,6,10..., и регистра CX: 1,2,4,8... Начало
цикла имеет метку, в данном случае, A20: - двоеточие обозначает, что метка
находится внутри процедуры (в данном случае BEGIN) в сегменте кода. В
конце цикла находится команда
JMP A20
которая указывает на то, что управление должно быть передано команде c
меткой A20. Обратите внимание, что адресная метка в операнде команды
указывается без двоеточия. Данный цикл не имеет выхода и приводит к
бесконечному выполнению - такие циклы обычно не используются.
__________________________________________________________________________
page 60,132
TITLE EXJUMP (COM) Организация цикла с помощью JMP
0000 CODESG SEGMENT PARA 'Code'
ASSUME CS:CODESG,DS:CODESG,SS:CODESG
0100 ORG 100H
0100 MAIN PROC NEAR
0100 B8 0001 MOV AX,01 ;Инициализация AX,
0103 BB 0001 MOV BX,01 ; BX,
0106 B9 0001 MOV CX,01 ; и CX
0109 A20:
0109 05 0001 ADD AX,01 ;Прибавить 01 к AX
010C 03 D8 ADD BX,AX ;Прибавить 01 к BX
010E D1 E1 SHL CX,1 ;Удвоить CX
0110 EB F7 JMP A20 ;Переход на A20
0112 MAIN ENDP
0112 CODESG ENDS
END MAIN
__________________________________________________________________________
Рис.7.1. Использование команды JMP.
Метку можно кодировать на одной строке с командой:
A20: ADD AX,01
или на отдельной строке:
A20:
ADD AX,01
В обоих случаях адрес A20 указывает на первый байт команды ADD.
Двоеточие в метке A20 указывает на тип метки - NEAR. Запомните: отсутствие
двоеточия в метке является частой ошибкой. В нашем примере A20
соответствует -9 байтам от команды JMP, в чем можно убедиться по
объектному коду команды - EBF7. EB представляет собой машинный код для
короткого перехода JMP, а F7 - отрицательное значение смещения (-9).
Команда JMP прибавляет F7 к командному указателю (IP), который содержит
адрес команды после JMP (0112):
Дес. Шест.
Командный указатель: 274 112
Адрес в команде JMP: -9 F7 (двоичное дополнение)
--- ---
Адрес перехода: 265 109
В результате сложения получается адрес перехода - шест.109. Проверьте
по листингу программы, что относительный адрес метки действительно
соответствует шест.109. Соответственно операнд в команде JMP для перехода
вперед имеет положительное значение.
Команда JMP для перехода в пределах -128 до +127 байт имеет тип
SHORT. Ассемблер генерирует в этом случае однобайтовый операнд в пределах
от 00 до FF. Команда JMP, превосходящая эти пределы, получает тип FAR, для
которого генерируется другой машинный код и двухбайтовый операнд.
Ассемблер в первом просмотре исходной программы определяет длину каждой
команды. Однако, команда JMP может быть длиной два или три байта. Если к
моменту просмотра команды JMP ассемблер уже вычислил значение операнда
(при переходе назад):
A50:
JMP A50
то он генерирует двухбайтовую команду. Если ассемблер еще не вычислил
значение операнда (при переходе вперед)
JMP A90
A90:
то он не знает тип перехода NEAR или FAR, и автоматически генерирует 3-х
байтовую команду. Для того, чтобы указать ассемблеру на необходимость
генерации двухбайтовой команды, следует использовать оператор SHORT:
JMP SHORT A90
A90:
В качестве полезного упражнения, введите программу, проассемблируйте
ее, скомпонуйте и переведите в COM-формат. Определение данных не
требуется, поскольку непосредственные операнды генерируют все необходимые
данные. Используйте отладчик DEBUG для пошагового выполнения COM-модуля и
просмотрите несколько повторений цикла. Когда регистр AX будет содержать
08, BX и CX увеличатся до шест.24 (дес.36) и шест.80 (дес.128),
соответственно. Для выхода из отладчика используйте команду Q.
КОМАНДА LOOP
________________________________________________________________
Команда JMP в примере на рис.7.1 реализует бесконечный цикл. Но более
вероятно подпрограмма должна выполнять определенное число циклов. Команда
LOOP, которая служит для этой цели, использует начальное значение в
регистре CX. В каждом цикле команда LOOP автоматически уменьшает
содержимое регистра CX на 1. Пока значение в CX не равно нулю, управление
передается по адресу, указанному в операнде, и если в CX будет 0,
управление переходит на следующую после LOOP команду.
__________________________________________________________________________
page 60,132
TITLE EXLOOP (COM) Организация цикла командой LOOP
0000 CODESG SEGMENT PARA 'Code'
ASSUME CS:CODESG,DS:CODESG,SS:CODESG
0100 ORG 100H
0100 BEGIN PROC NEAR
0100 B8 0001 MOV AX,01 ;Инициализация AX,
0103 BB 0001 MOV BX,01 ; BX,
0106 BA 0001 MOV DX,01 ; и DX
0109 B9 000A MOV CX,10 ;Число циклов
010C A20:
010C 40 INC AX ;Прибавить 01 к AX
010D 03 D8 ADD BX,AX ;Прибавить AX к BX
010F D1 E2 SHL DX,1 ;Удвоить DX
0111 E2 F9 LOOP A20 ;Уменьшить CX и повторить
; цикл, если не нуль
0113 C3 RET ;Завершить работу
0114 BEGIN ENDP
0114 CODESG ENDS
END BEGIN
__________________________________________________________________________
Рис.7.2. Использование команды LOOP.
Программа на рис.7.2, иллюстрирующая использование команды LOOP,
выполняет действия, аналогичные примеру на рис.7.1 за исключением того,
что после десяти циклов программа завершается. Команда MOV инициализирует
регистр CX значением 10. Так как команда LOOP использует регистр CX, то в
программе для удвоения начального значения 1 вместо регистра CX
используется DX. Команда JMP A20 заменена командой LOOP и для
эффективности команда ADD AX,01 заменена командой INC AX (увеличение AX на
1).
Аналогично команде JMP, операнд команды LOOP определяет расстояние от
конца команды LOOP до адреса метки A20, которое прибавляется к содержимому
командного указателя. Для команды LOOP это расстояние должно быть в
пределах от -128 до +127 байт. Если операнд превышает эти границы, то
ассемблер выдаст сообщение "Relative jump out of range" (превышены границы
перехода).
Для проверки команды LOOP рекомендуется изменить соответствующим
образом программу, приведенную на рис.7.1, выполнить ее ассемблирование,
компоновку и преобразование в COM-файл. Для трассировки всех десяти циклов
используйте отладчик DEBUG. Когда в значение регистре CX уменьшится до
нуля, содержимое регистров AX, BX и DX будет соответственно шест. 000B,
0042 и 0400. Для выхода из отладчика введите команду Q.
Дополнительно существует две разновидности команды LOOP - это LOOPE
(или LOOPZ) и LOOPNE (или LOOPNZ). Обе команды также уменьшают значение
регистра CX на 1. Команда LOOPE передает управление по адресу операнда,
если регистр CX имеет ненулевое значение и флаг нуля установлен (ZF=1).
Команда LOOPNE передает управление по адресу операнда, если регистр CX
имеет ненулевое значение и флаг нуля сброшен (ZF=0).
ФЛАГОВЫЙ РЕГИСТР
________________________________________________________________
Следующий материал данной главы требует более детального ознакомления
с флаговым регистром. Этот регистр содержит 16 бит флагов, которые
управляются различными командами для индикации состояния операции. Во всех
случаях флаги сохраняют свое значение до тех пор, пока другая команда не
изменит его. Флаговый регистр содержит следующие девять используемых бит
(звездочками отмечены неиспользуемые биты):
Номер бита: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Флаг: * * * * O D I T S Z * A * P * C
Рассмотрим эти флаги в последовательности справа налево.
CF (Carry Flag) - флаг переноса. Содержит значение "переносов" (0 или
1) из старшего разряда при арифметических операциях и некоторых операциях
сдвига и циклического сдвига (см. гл.12).
PF (Parity Flag) - флаг четности. Проверяет младшие восемь бит
результатов операций над данными. Нечетное число бит приводит к установке
этого флага в 0, а четное - в 1. Не следует путать флаг четности с битом
контроля на четность.
AF (Auxiliary Carry Flag) - дополнительный флаг переноса.
Устанавливается в 1, если арифметическая операция приводит к переносу
четвертого справа бита (бит номер 3) в регистровой однобайтовой команде.
Данный флаг имеет отношение к арифметическим операциям над символами кода
ASCII и к десятичным упакованным полям.
ZF (Zero Flag) - флаг нуля. Устанавливается в качестве результата
арифметических команд и команд сравнения. Как это ни странно, ненулевой
результат приводит к установке нулевого значения этого флага, а нулевой -
к установке единичного значения. Кажущееся несоответствие является,
однако, логически правильным, так как 0 обозначает "нет" (т.е. результат
не равен нулю), а единица обозначает "да" (т.е. результат равен нулю).
Команды условного перехода JE и JZ проверяют этот флаг.
SF (SIgn Flag) - знаковый флаг. Устанавливается в соответствии со
знаком результата (старшего бита) после арифметических операций:
положительный результат устанавливает 0, а отрицательный - 1. Команды
условного перехода JG и JL проверяют этот флаг.
TF (Trap Flag) - флаг пошагового выполнения. Этот флаг вам уже
приходилось устанавливать, когда использовалась команда Т в отладчике
DEBUG. Если этот флаг установлен в единичное состояние, то процессор
переходит в режим пошагового выполнения команд, т.е. в каждый момент
выполняется одна команда под пользовательским управлением.
IF (Interrupt Flag) - флаг прерывания. При нулевом состоянии этого
флага прерывания запрещены, при единичном - разрешены.
DF (DIrection Flag) - флаг направления. Используется в строковых
операциях для определения направления передачи данных. При нулевом
состоянии команда увеличивает содержимое регистров SI и DI, вызывая
передачу данных слева направо, при нулевом - уменьшает содержимое этих
регистров, вызывая передачу данных справа налево (см. гл.11).
OF (Overflow Flag) - флаг переполнения. Фиксирует арифметическое
переполнение, т.е. перенос вниз старшего (знакового) бита при знаковых
арифметических операциях.
В качестве примера: команда CMP сравнивает два операнда и
воздействует на флаги AF, CF, OF, PF, SF, ZF. Однако, нет необходимости
проверять все эти флаги по отдельности. В следующем примере проверяется
содержит ли регистр BX нулевое значение:
CMP BX,00 ;Сравнение BX с нулем
JZ B50 ;Переход на B50 если нуль
. (действия при не нуле)
B50: ... ;Точка перехода при BX=0
Если BX содержит нулевое значение, команда CMP устанавливает флаг
нуля ZF в единичное состояние, и возможно изменяет (или нет) другие флаги.
Команда JZ (переход, если нуль) проверяет только флаг ZF. При единичном
значении ZF, обозначающее нулевой признак, команда передает управление на
адрес, указанный в ее операнде, т.е. на метку B50.
КОМАНДЫ УСЛОВНОГО ПЕРЕХОДА
________________________________________________________________
В предыдущих примерах было показано, что команда LOOP уменьшает на
единицу содержимое регистра CX и проверяет его: если не ноль, то
управление передается по адресу, указанному в операнде. Таким образом,
передача управления зависит от конкретного состояния. Ассемблер
поддерживает большое количество команд условного перехода, которые
осуществляют передачу управления в зависимости от состояний флагового
регистра. Например, при сравнении содержимого двух полей последующий
переход зависит от значения флага.
Команду LOOP в программе на рис.7.2 можно заменить на две команды:
одна уменьшает содержимое регистра CX, а другая выполняет условный
переход:
Использование LOOP Использование условного перехода
LOOP A20 DEC CX
JNZ A20
Команды DEC и JNZ действуют аналогично команде LOOP: уменьшают
содержимое регистра CX на 1 и выполняет переход на метку A20, если в CX не
ноль. Команда DEC кроме того устанавливает флаг нуля во флаговом регистре
в состояние 0 или 1. Команда JNZ затем проверяет эту установку. В
рассмотренном примере команда LOOP хотя и имеет ограниченное
использование, но более эффективна, чем две команды: DEC и JNZ.
Аналогично командам JMP и LOOP операнд в команде JNZ содержит
значение расстояния между концом команды JNZ и адресом A20, которое
прибавляется к командному указателю. Это расстояние должно быть в пределах
от -128 до +127 байт. В случае перехода за эти границы ассемблер выдаст
сообщение "Relative jump out of range" (превышены относительные границы
перехода).
Знаковые и беззнаковые данные
-------------------------------
Рассматривая назначение команд условного перехода следует пояснить
характер их использования. Типы данных, над которыми выполняются
арифметические операции и операции сравнения определяют какими командами
пользоваться: беззнаковыми или знаковыми. Беззнаковые данные используют
все биты как биты данных; характерным примером являются символьные строки:
имена, адреса и натуральные числа. В знаковых данных самый левый бит
представляет собой знак, причем если его значение равно нулю, то число
положительное, и если единице, то отрицательное. Многие числовые значения
могут быть как положительными так и отрицательными.
В качестве примера предположим, что регистр AX содержит 11000110, а
BX - 00010110. Команда
CMP AX,BX
сравнивает содержимое регистров AX и BX. Если данные беззнаковые, то
значение в AX больше, а если знаковые - то меньше.
Переходы для беззнаковых данных
---------------------------------
Мнемоника Описание Проверяемые флаги
JE/JZ Перех
...Поділитися у соцмережах