Сист. прогр. Ч1 (1085770), страница 8
Текст из файла (страница 8)
Так как первый операнд всегда предполагает ЕХЕ-файл, то можно не кодировать тип ЕХЕ. Второй операнд может иметь другое имя (не CALC.COM). Если не указывать тип СОМ, то EXE2BIN примет по умолчанию тип BIN, который впоследствии можно переименовать в СОМ. После того как преобразование будет выполнено, можно удалить OBJ- и ЕХЕ-файлы.
Если исходная программа написана для ЕХЕ-формата, то можно, используя редактор, заменить команды в исходном тексте для СОМ-фанла.
ПРИМЕР СОМ-ПРОГРАММЫ
Программа ЕХСОМ1, приведенная на рис. 5.1, аналогична программе на рис. 4.3, но изменена согласно требованиям СОМ-формата. Обратите внимание на следующие изменения в этой СОМ-программе:
-
Стек и сегмент данных отсутствуют.
-
Оператор ASSUME указывает ассемблеру на необходимость установить относительные адреса с начала сегмента кодов. Регистр CS также содержит этот адрес, являющийся к тому же адресом префикса программного сегмента (PSP). Директива ORG служит для резервирования 100Н байт от начального адреса под PSP.
-
Директива ORG 100H устанавливает относительный адрес для начала выполнения программы. Программный загрузчик устанавливает этот адрес в командном указателе.
-
Команда JMP служит для обхода данных, определенных в программе.
Для обработки и выполнения этой программы необходимы следующие шаги:
MASM [ответы на запросы обычные]
LINK [ответы на запросы обычные]
EXE2BIN В:ЕХСОМ1,В:ЕХСОМ1.СОМ
DEL B:EXCOM1.OBJ,B:EXCOM1.EXE [удаление OBJ- и ЕХЕ-файлов]
Размеры ЕХЕ- и СОМ-программ - 788 и 20 байт соответственно. Учитывая такую эффективность СОМ-файлов, рекомендуется все небольшие программы создавать в СОМ-формате. Для трассировки выполнения программы от начала и до команды RET (но не включая ее) введите DEBUG В:ЕХСОМ1.СОМ.
Некоторые программисты кодируют элементы данных после команд так, что первая команда JMP не требуется. Кодирование элементов данных перед командами позволяет ускорить процесс ассемблирования и является хорошим стилем, рекомендуемым в руководствах по ассемблеру.
Page 60,132
TITLE XCOM1 СОM ; для пересылки и сложения
CODESG SEGMENT PARA ‘'Code’
ASSUME CS:CODESG, DS:CODESG, SS:CODESG, ES:CODESG .
ORG 100H ;Начало в конце PSP
BEGIN: JMP MAIN ;0бход через данные
FLDA DW 250 ;Определение данных
FLDB DW 125
FLDC DW ?
MAIN PROC NEAR
MOV AX, FLDA ;.-Переслать 0250 в АХ
ADD AX, FLDB ;Прибавить 0125 к АХ
MOV FLDC,AX ; Записать сумму в FLDC
RET ;Вернуться в DOS
MAIN ENDP
CODESG ENDS
END BEGIN
Рис. 5.1 Пример СОМ-программы
СТЕК ДЛЯ СОМ-ПРОГРАММЫ
Для СОМ-файла DOS автоматически определяет стек и устанавливает одинаковый, общий сегментный адрес во всех четырех сегментных регистрах. Если для программы размер сегмента в 64 К достаточен, то DOS устанавливает в регистре SP адрес конца сегмента - FFFE. Это будет вершина стека. Если 64-Кбайтный сегмент не имеет достаточно места для стека, то DOS устанавливает стек в конце памяти. В обоих случаях DOS записывает затем в стек нулевое слово.
Возможность использования стека зависит от размера программы и ограниченности памяти. С помощью команды DIR можно определить размер файла и вычислить необходимое пространство для стека.
Все небольшие программы в этой книге в основном рассчитаны на СОМ-формат.
ОСОБЕННОСТЬ ОТЛАДКИ
Несоблюдение хотя бы одного требования СОМ-формата может послужить причиной неправильной работы программы. Если EXE2BIN обнаруживает ошибку, то выдается сообщение о невозможности преобразования файла без указания конкретной причины. Необходимо проверить в этом случае директивы SEGMENT, ASSUME и END. Если опущен ORG 100H, то на данные в префиксе программного сегмента будут установлены неправильные ссылки с непредсказуемым результатом при выполнении.
При выполнении СОМ-программы под управлением отладчика DEBUG необходимо использовать команду D CS:100 для просмотра данных и команд. Не следует выполнять в отладчике команду RET; предпочтительнее использовать команду Q отладчика. Некоторые программисты используют INT 20Н вместо команды RET.
Попытка выполнить ЕХЕ-модуль программы, написанной для СОМ-формата, успеха не имеет.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ
Объем СОМ-файла ограничен 64 Кбайт.
Файл в СОМ-формате меньше, чем соответствующий ЕХЕ- файл.
Программа, написанная для выполнения в СОМ-формате, не содержит стека и сегмента данных и не требует инициализации регистра DS.
Программа, написанная для выполнения в СОМ-формате,
использует директиву ORG 100H после директивы SEGMENT для выполнения с адреса после префикса программного сегмента.
-
Программа EXE2BIN преобразует ЕХЕ-файл в СОМ-файл, обусловленный указанием типа СОМ во втором перанде.
-
Операционная система DOS определяет стек для СОМ-
программы или в конце программы, если позволяет размер,
или в конце памяти. .
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ
Каков максимальный размер СОМ-файла?
Какие сегменты можно определить в программе, которая будет преобразована в СОМ-файл?
Определение стека в СОМ-файлах отсутствует. Как ведет себя СОМ-файл с учетом этого обстоятельства?
Программа в результате компоновки получила имя SAMPLE.EXE. Напишите команду DOS для преобразования ее в СОМ-файл.
6. КОМАНДЫ ПЕРЕХОДА
Некоторые команды могут передавать управление, изменяя
нормальную последовательность шагов непосредственной модификацией значения смещения в командном указателе. Ниже
приведены четыре способа передачи управления, которые будут
рассмотрены в этой гласе:
Безусловный переход: JMP
Цикл: LOOP
Условный переход: Jnnn (больше, меньше, равно)
Вызов процедуры: CALL
Заметим, что имеется три типа адресов: SHORT, NEAR и FAR. Адресация SHORT используется при циклах, условных переходах и некоторых безусловных переходах. Адресация NEAR и FAR используется для вызовов процедур (CALL) и безусловных переходов, которые не квалифицируются как SHORT. Все три типа передачи управления воздействуют на содержимое регистра IP; тип FAR- также изменяет регистр CS.
КОМАНДА JMP
Одна из команд, обычно используемых для передачи управления-команда JMP. Эта команда выполняет безусловный переход, т.е. обеспечивает передачу управления при любых обстоятельствах.
В СОМ-программе на рис.6.1 используется команда JMP. В регистры АХ, ВХ, и СХ загружается значение 1, и затем в цикле выполняются следующие операции:
прибавить 1 к регистру АХ,
прибавить АХ к ВХ,
удвоить значение в регистре СХ.
Повторение цикла приводит к увеличению содержимого регистра АХ: 1,2,3,4..., регистра ВХ: 1,3,6,10..., регистра СХ: 1,2,4,8... Начало цикла имеет метку, в данном случае А20: - двоеточие обозначает, что метка находится внутри процедуры (в данном случае BEGIN) в сегменте кода. В конце цикла находится команда
JMP A20,
указывающая на то, что управление должно быть передано команде с меткой А20. Обратите внимание, адресная метка в операнде команды указывается без двоеточия. Данный цикл не имеет выхода и приводит к бесконечному выполнению - такие циклы обычно не используются.
page 60,132
TITLE EXJUMP (СОM) ; Организация цикла с помощью JMP CODESG SEGMENT PARA 'Code'
ASSUME: CS:CODESG, DS:CODESG, SS:CODESG
ORG 100H
0100 MAIN PROC NEAR
0100 В8 0001 MOV AX, 01
0103 ВВ 0001 MOV BX, 01
0106 В9 0001 MOV CX,01
0109 А20:
0109 05 0001 ADD AX, 01
010C 03 D8 ADD BX.,AX
010Е Dl E1 SHL CX,1
0110 ЕВ F7 JMP A20
0112 MAIN ENDP
0112 CODESG ENDS
END MAIN
Рис.6.1. Использование команды JMP
Метку можно кодировать на одной строке с командой:
А20: ADD AX,01
или на отдельной строке:
А20:
ADD AX,01
В обоих случаях адрес А20 указывает на первый байт команды ADD. Двоеточие в метке А20 означает тип метки - NEAR. Запомните, что отсутствие двоеточия в метке является частой ошибкой. В нашем примере А20 соответствует -9 байтам от команды JMP, в чем можно убедиться по объектному коду команды - EBF7. Здесь ЕВ представляет собой машинный код для короткого перехода JMP, a 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 ассемблер уже вычислил значение операнда (при переходе назад)
А50:
………………..
JMP A50
то он генерирует двухбайтовую команду. Если ассемблер еще не вычислил значение операнда (при переходе вперед):
JMP A90
………..
А90:
то он не знает тип перехода NEAR или FAR и автоматически генерирует трехбайтовую команду. Для того, чтобы указать ассемблеру на необходимость генерации двухбайтовой команды, следует использовать оператор SHORT:
JMP SHORT A90
………
А90:
В качестве упражнения введите программу, проассемблируйте ее, скомпонуйте и переведите в СОМ-формат. Определение данных не требуется, поскольку непосредственные операнды генерируют все необходимые данные. Используйте отладчик DEBUG для пошагового выполнения СОМ-модуля и просмотрите несколько повторений цикла. Когда регистр АХ будет содержать 08, ВХ и СХ увеличатся до 24Н (36) и З0Н (128) соответственно. Для выхода из отладчика используйте команду Q.
КОМАНДА LOOP
Команда JMP в примере на рис.6.1 реализует бесконечный цикл. Но более вероятно подпрограмма должна выполнять определенное количество циклов. Команда LOOP, применяемая в этом случае, использует начальное значение в регистре СХ. В каждом цикле команда LOOP автоматически уменьшает содержимое регистра СХ на 1. Пока значение в СХ не равно нулю, управление передается по адресу, указанному в операнде, и если в СХ будет 0, управление переходит на следующую после LOOP команду.
page 60,132
TITLE EXLOOP (СОM) ; Организация цикла командой 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 ; Инициализация АХ,
0103 BB 0001 MOV BX, 01 ; BX,
0106 BA 0001 MOV DX, 01 ; и DX
0109 B9 OOOA MOV CX, 10 ; Число циклов
010C A20:
010C 40 INC AX ; Прибавить 01 к АХ
0100 03 D8 ADD BX, AX ; Прибавить АХ к ВХ
010F D1 E2 SHL DX, l ; Удвоить DХ
0111 Е2 F9 LOOP A20 ; Уменьшить СХ и повторить
; цикл, если не нуль
0113 C3 RET ; Завершить работу
0114 BEGIN ENDP
0114 COOESG ENDS
END BEGIN
Рис. 6.2. Использование команды LOOP
Программа на рис. 6.2, иллюстрирующая использование команды LOOP, выполняет действия, аналогичные приведенным в примере на рис.6.1, за исключением того, что после десяти циклов программа завершается. Команда MOV инициализирует регистр СХ значением 11. Так как команда LOOP использует регистр СХ, то в программе для удвоения начального значения 1 вместо регистра СХ используется DX. Команда JMP A20 заменена командой LOOP, и для эффективности команда ADD AX,01 заменена командой INC АХ (увеличение АХ на 1). Аналогично команде JMP, операнд команды LOOP определяет расстояние от конца команды LOOP до адреса метки А20, которое прибавляется к содержимому командного указателя. Для команды LOOP это расстояние должно быть в пределах от -128 до +127 байт. Если операнд превышает эти границы, то ассемблер выдаст сообщение "Relative jump out of range" (Превышение относительной границы перехода).
Для проверки команды LOOP рекомендуется изменить соответствующим образом программу, приведенную на рис.6.1, выполнить ее ассемблирование, компоновку и преобразование в СОМ-файл. Для трассировки всех десяти циклов используйте отладчик DEBUG. Когда значение в регистре СХ уменьшится до нуля, содержимое регистров АХ, ВХ и DX будет соответственно 000ВН, 0042Н и 0400Н. Для выхода из отладчика введите команду Q.
Дополнительно существует две разновидности команды LOOP - это LOOPE (или LOOPZ) и LOOPNE (или LOOPNZ). Обе команды также уменьшают значение регистра СХ на 1. Команда LOOPE передает управление по адресу операнда, если регистр СХ имеет ненулевое значение и флаг нуля установлен (ZF= 1). Команда LOOPNE передает управление по адресу операнда, если регистр СХ имеет ненулевое значение и флаг нуля сброшен (ZF = 0).