Структура com-программ

COM-программы состоят только из одного сегмента – сегмента кода. Следовательно, размер такой программы не может превышать 64 Кбайт (EXE-программа может иметь любой размер). Стек в COM-программе генерируется автоматически и также располагается в сегменте кода. Он располагается в конце сегмента и растет вверх – в сторону младших адресов. При большом размере стека он может затереть код. Данные также должны определяться в сегменте кода.

Для работы COM-программы (см. рис.6.3) также необходим префикс программного сегмента. Однако поскольку сегмент всего один, то и PSP также размещается в сегменте кода. Для того, чтобы код не наложился на префикс, необходимо чтобы адресация в сегменте кода начиналась со смещения 100H. Для этого сразу после директивы SEGMENT (обязательно перед первой выполняемой командой) должна располагаться директива ORG 100H. Эта директива помещает в регистр указатель команд IP значение 100H, по которому и будет располагаться первая выполняемая команда, а место под PSP использоваться не будет. Если после директивы ORG 100H располагаются данные, то их необходимо обойти, используя, например, команду условного перехода. В противном случае эти данные при передаче на них управления будут интерпретированы как команда, что, скорее всего, приведет к ошибке.

Поскольку главная процедура будет вызываться из сегмента, в котором она была определена (поскольку PSP находится в этом же сегменте), то она должна иметь тип NEAR.

Начальный адрес префикса программного сегмента в данном случае находится в регистрах CS, SS или DS (они все содержат одинаковое значение, так как сегмент один) и, следовательно, дополнительно где-либо сохранять его не нужно.

Запись в стек нулевого значения (которое будет записано в регистр IP командой RET для перехода на начало PSP и возврата в MS-DOS) также осуществляется операционной системой. Естественно, что перед выполнением команды RET стек должен быть приведен в исходное состояние.

Соответствие между единственным сегментом и сегментными регистрами также осуществляется директивой ASSUME. Пример записи директивы ASSUME для COM-программ:

ASSUME CS:codesg,DS:codesg,SS:codesg,ES:nothing

Пример оформления COM-программы приведен на рис.6.4.

структура com-программ - student2.ru

Рис. 6.3. Состояние памяти при загрузке COM-программы

codesg SEGMENT PARA 'Code'

ASSUME CS:codesg,DS:codesg,SS:codesg,ES:codesg

ORG 100H ;Обход PSP

begin: JMP main ;Обход через данные

; ---------------------------------------------------

fld1 DB 45 ;Числовая константа

fld2 DB 17 ;Числовая константа

fld3 DB ? ;Числовая константа

; ---------------------------------------------------

main PROC NEAR

MOV AL, fld1 ;Переслать 45D в AL

ADD AL, fld2 ;Прибавить 17D к AX

MOV fld3,AL ;Записать сумму в fld3

RET

main ENDP

codesg ENDS

END begin

Рис. 6.4. Пример оформления COM-программы

Поскольку в COM-программах данные также определяются в сегменте кода, то они изменяют регистр указатель команд IP. Следовательно, для определения того объема памяти, который занимают данные, можно использовать счетчик размещения (см. разд.4). Особенно эффективно это делать для определения длины массивов.

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

fld1 DB 12, 34, 71, 13, 45

len1 = $ – fld1; Длина массива равна 5

Аналогичный пример с символьными данными (каждый символ – это тоже байт в коде ASCII) будет выглядеть следующим образом:

fld2 DB 'Hello, world'

len2 = $ – fld2; Длина массива равна 12

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

fld3 DW 56, 12, 41H, 99

len3 = $ – fld3; Длина массива равна 8

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

Вопросы для самопроверки

1. В чем различие между COM-программами и EXE-программами?

2. Почему главная процедура EXE-программы должна быть типа FAR? Что произойдет если она будет иметь тип FAR?

3. Зачем при загрузке EXE-программы значение сегментного регистра сохраняется в стеке?

4. Может ли сегмент стека EXE-программы занимать 64 Кбайт?

5. Может ли сегмент стека COM-программы занимать 64 Кбайт?

6. Могут ли данные определяться в сегменте кода?

7. Что произойдет, если после директивы ORG 100H в COM-программе будет следовать не первая исполняемая команда, а данные (не будет команды условного перехода JMP)?

8. Чему равно значение переменной len при выполнении данного фрагмента кода?

fld1 DW 33, 32

fld2 DB 'Cat'

fld3 DB 10 DUP(12)

len = $ – fld1

Наши рекомендации