Assembler / P20
.pdf11
|
?debug |
C E9 |
|
|
?debug |
C FA00000000 |
|
_BSS |
ends |
|
|
_DATA |
segment word public 'DATA' |
||
s@ |
label byte |
|
|
|
db |
'%d ' |
|
|
db |
0 |
|
|
db |
10 |
|
|
db |
10 |
|
|
db |
0 |
|
|
db |
'%d ' |
|
|
db |
0 |
|
|
db |
10 |
|
|
db |
0 |
|
|
db |
'%p ' |
|
|
db |
0 |
|
_DATA ends
_TEXT segment byte public 'CODE' _TEXT ends
extrn _printf:near
public |
_A |
public |
_B |
public |
_main |
end |
|
Внимательно изучите этот файл. Нужно пояснить, что в сегменте данных DATA собраны инициализированные данные, а в BSS — неинициализированные.
Теперь оттранслируем программу с помощью 32-разрядного компилятора. bcc32 –S c31v0.c
Будет получен файл c31v0.asm. Переименуем его в c31v0_32.asm.
.386p |
|
model flat |
|
ifndef |
??version |
?debug |
macro |
endm |
|
endif |
|
?debug |
S "c3v0.c" |
?debug |
T "c3v0.c" |
_TEXT |
segment dword public use32 'CODE' |
|
_TEXT |
ends |
|
_DATA |
segment dword public use32 'DATA' |
|
_DATA |
ends |
|
_BSS |
segment dword public use32 'BSS' |
|
_BSS |
ends |
|
DGROUP |
group _BSS,_DATA |
|
_DATA segment dword public use32 'DATA'
|
align |
4 |
_A |
label |
dword |
|
dd |
1 |
|
dd |
5 |
|
dd |
2 |
|
dd |
7 |
|
dd |
5 |
|
dd |
6 |
|
dd |
3 |
_DATA ends
_TEXT segment dword public use32 'CODE' _main proc near
?live1@0:
;
; int main() {
;
push ebp
12
mov |
ebp,esp |
push |
ebx |
push |
esi |
push |
edi |
mov |
esi,offset _A |
;
;int i, j, k;
;for (i = 0; i < dimA; i++)
?live1@16: ; ESI = &_A @1:
xor ebx,ebx
;
; printf("%d ", A[i]);
;
?live1@32: ; EBX = i, ESI = &_A @2:
|
push |
dword ptr [esi+4*ebx] |
|
push |
offset s@ |
|
call |
_printf |
|
add |
esp,8 |
|
inc |
ebx |
|
cmp |
ebx,7 |
|
jl |
short @2 |
; |
|
|
; |
|
|
; |
i = j = dimA - 1; k = 0; |
|
; |
|
|
?live1@48: ; ESI = &_A
|
mov |
eax,6 |
|
mov |
ebx,eax |
|
xor |
edi,edi |
; |
|
|
; |
while (j > 0) { |
|
;
?live1@64: ; EBX = i, EAX = j, ESI = &_A, EDI = k
test |
eax,eax |
|
jle |
short |
@6 |
; |
|
|
; |
if (A[j] == |
5) { |
; |
|
|
@5: |
|
|
cmp |
dword |
ptr [esi+4*eax],5 |
jne |
short |
@7 |
; |
|
|
; |
B[k++] = &A[j]; |
|
; |
|
|
mov |
edx,eax |
|
shl |
edx,2 |
|
add |
edx,esi |
|
mov |
dword |
ptr [_B+4*edi],edx |
inc |
edi |
|
jmp |
short |
@9 |
; |
|
|
;while (j > 0 && A[--j] != 5) {
;A[i--] = A[j];
; |
|
@8: |
|
mov |
ecx,dword ptr [esi+4*eax] |
mov |
dword ptr [esi+4*ebx],ecx |
dec |
ebx |
@9: |
|
test |
eax,eax |
|
13 |
jle |
short @10 |
dec |
eax |
cmp |
dword ptr [esi+4*eax],5 |
jne |
short @8 |
; |
|
; |
} |
; |
if (j == 0 && A[0] == 5) |
; |
|
@10: |
|
test |
eax,eax |
jne |
short @12 |
cmp |
dword ptr [esi],5 |
jne |
short @12 |
; |
|
; |
B[k++] = &A[j]; |
; |
|
mov |
edx,eax |
shl |
edx,2 |
add |
edx,esi |
mov |
dword ptr [_B+4*edi],edx |
inc |
edi |
; |
|
; |
} |
; |
|
jmp |
short @12 |
; |
|
;else {
;i--;
@7: |
|
dec |
ebx |
; |
|
; |
j--; |
; |
|
dec |
eax |
@12: |
|
test |
eax,eax |
jg |
short @5 |
; |
|
;}
;}
;for (j = 0; j <= i; j++)
?live1@240: ; EBX = i, ESI = &_A, EDI = k @6:
xor |
eax,eax |
cmp |
ebx,eax |
jl |
short @14 |
;
; A[j] = -1;
;
?live1@256: ; EBX = i, EAX = j, ESI = &_A, EDI = k @13:
mov |
dword ptr [esi+4*eax],-1 |
inc |
eax |
cmp |
ebx,eax |
jge |
short @13 |
;
; printf("\n\n");
;
?live1@272: ; ESI = &_A, EDI = k @14:
push |
offset s@+4 |
call |
_printf |
14
pop ecx
;
; for (i = 0; i < dimA; i++)
;
xor ebx,ebx
;
; printf("%d ", A[i]);
;
?live1@304: ; EBX = i, ESI = &_A, EDI = k @16:
|
push |
dword |
ptr [esi+4*ebx] |
|
push |
offset s@+7 |
|
|
call |
_printf |
|
|
add |
esp,8 |
|
|
inc |
ebx |
|
|
cmp |
ebx,7 |
|
|
jl |
short |
@16 |
; |
|
|
|
; |
printf("\n"); |
|
|
; |
|
|
|
?live1@320: ; EDI = k |
|
||
|
push |
offset s@+11 |
|
|
call |
_printf |
|
|
pop |
ecx |
|
; |
|
|
|
; |
for (i = 0; i |
< k; i++) |
|
; |
|
|
|
|
xor |
ebx,ebx |
|
|
cmp |
edi,ebx |
|
|
jle |
short |
@20 |
;
; printf("%p ", B[i]);
;
?live1@352: ; EBX = i, EDI = k @19:
push |
dword ptr [_B+4*ebx] |
push |
offset s@+13 |
call |
_printf |
add |
esp,8 |
inc |
ebx |
cmp |
edi,ebx |
jg |
short @19 |
;
; return 0;
;
?live1@368: ; @20:
|
xor |
|
eax,eax |
; |
|
|
|
; |
} |
|
|
; |
|
|
|
@23: |
|
|
|
@22: |
|
|
|
|
pop |
|
edi |
|
pop |
|
esi |
|
pop |
|
ebx |
|
pop |
|
ebp |
|
ret |
|
|
_main endp |
|
|
|
_TEXT ends |
|
|
|
_BSS |
segment dword public use32 'BSS' |
||
|
align 4 |
|
|
_B |
label dword |
||
|
db |
28 |
dup(?) |
|
|
15 |
_BSS |
ends |
|
_DATA |
segment dword public use32 'DATA' |
|
s@ |
label byte |
|
|
; |
s@+0: |
|
db |
"%d ",0,10,10,0 |
|
; |
s@+7: |
|
db |
"%d ",0,10,0 |
|
; |
s@+13: |
|
db |
"%p ",0 |
|
align |
4 |
_DATA ends
_TEXT segment dword public use32 'CODE' _TEXT ends
public |
_A |
public |
_B |
public |
_main |
extrn _printf:near |
|
?debug |
D "C:\BC5\INCLUDE\_null.h" 8825 10304 |
?debug |
D "C:\BC5\INCLUDE\_nfile.h" 8825 10304 |
?debug |
D "C:\BC5\INCLUDE\_defs.h" 8825 10304 |
?debug |
D "C:\BC5\INCLUDE\stdio.h" 8825 10304 |
?debug |
D "c31v0.c" 12910 21959 |
end |
|
Отладку файла c31v0.exe нужно проводить с помощью 32-разрядного отладчика: td32.exe c31v0.exe
20.7. Программы на языке Си, размещенные в нескольких файлах.
Большие программы на языке Си создает бригада программистов. Они распределяют между собой работу по написанию подпрограмм. Каждый программист создает свой файл с подпрограммами. Далее их нужно собрать вместе. Порядок работы здесь следующий.
Если в файлах нет ссылки на общие глобальные данные (Глобальных данных следует избегать), то нужно только обеспечить правильный вызов функций.
20.8. Исполняемые файлы формата com
Мы уже создавали com-файлы с помощью отладчиков. Посредством tasm и tlink мы создавали только exe-файлы. Теперь с их помощью научимся получать и .com-файлы. Расширение файла com расшифровывается так: COre iMage. Вольный перевод: образ в оперативной памяти. Мы видели, что exe-файлы содержат блок управляющей и настроечной информации, в листинге отмечалось специальными флажками, где коды объектного и загрузочного файлов будут различными. Содержимое com-файла — в точности те же коды, которые будут расположены после загрузки в оперативной памяти. Эти файлы занимают меньше дисковой памяти, быстрее загружаются в ОЗУ, но при их создании надо придерживаться некоторых ограничений.
Различия между exe- и com-файлами сведем в таблицу (табл. *.1)
Таблица *.1
Свойство |
exe-файл |
com-файл |
Размер |
Любой |
Не превышает 64 Кбайта |
программы |
|
|
Стек |
Определяется сегмент стека. |
Стек определяется при загрузке. |
|
|
SP = FFFE. |
Данные |
Обычно определяется сегмент |
Данные размещаются в кодовом |
|
данных. |
сегменте. |
Содержимое |
CS — указывает на сегмент кода, |
CS, SS, DS, ES — |
сегментных |
SS — указывает на сегмент |
указывают на PSP |
16
регистров |
стека, |
DS, ES — указывают на PSP
Итак, из первой строки таблицы ясно, что размер com-файла не превышает 64K. Но в Windows 95 размер файла command.com составляет 92870 байтов! На самом деле это exeфайл (нетрудно убедиться, что его первые два байта образуют сигнатуру 'MZ') и расширение .com ему присваивают по традиции.
Прежде чем приводить пример .com-программы, познакомимся с новой для нас директивой Ассемблера.
ORG выражение — вычисляет выражение и присваивает полученное значение счетчику адреса. (Напомню, что счетчик адреса хранится во встроенной переменной $.)
Пример программы повторяет нашу первую программу, но в новом формате:
pc.asm |
|
|
|
INCLUDE macro.inc |
|
|
.MODEL tiny |
|
|
.CODE |
|
|
ORG 100h |
; Выделить пространство для PSP |
start: |
jmp short begin |
; "Обойти" данные |
msg |
DB "Hello!",0Dh,0Ah,'$' |
|
begin: message msg exit
END start
Директива ORG 100h выделяет 256 байтов для префикса программного сегмента. (Можно было вместо этой директивы написать $ = $ + 100h или DB 100h DUP(?).) При написании программ в exe-формате заботиться об этом не было необходимости.
Данные рекомендуется помещать перед кодом, чтобы ассемблеру было легче сгенерировать нужные коды команд. Но по смещению 100h обязательно должна находиться команда, а не данные. Поэтому инструкция безусловного перехода передает управление на первую "настоящую" команду кода. Сегментный регистр DS теперь загружать не нужно. Программу можно было завершить не вызовом функции 4Ch, а прерыванием int 20h, специально предназначенным для завершения com-программ. (Для int 20h регистр CS должен указывать на PSP. Но для com-программ это и так выполнено.)
Последовательность действий для получения com-файла. Нужно выполнить команды:
tasm pc.asm tlink/t pc.obj
Ключ /t указывает tlink, что нужно создавать pc.com, а не pc.exe. (t — сокращение от tiny.) Упражнение. Какое сообщение об ошибке выдаст tlink, если вы опустите директиву
ORG 100h? если вы включите директиву .STACK 100h? Упражнение. Сравните размеры файлов p.exe (гл. ) и p.com. С помощью MASM файл pc.com можно получить так:
ml/AT pc.asm
В ранних версиях MASM сначала нужно было получить exe-файл, а затем преобразовать его в com-файл с помощью утилиты exe2bin.
Отладка com-файлов. Если, как раньше, добавить к tasm и tlink ключи /zi и /v, то, к нашему разочарованию, td сообщит нам: "Program has no symbol table". Действуем подругому: сначала получим pc.exe, а затем преобразуем его в pc.com, размещая отладочную информацию в отдельном файле.
tasm/zi pc.asm
17
tlink/v pc.obj (создает pc.exe, выдает предупреждение: No stack — игнорируем его) tdstrip –s –c pc.exe
td pc.com
Утилита tdstrip переносит отладочную информацию из pc.exe в файл pc.tds (ключ –s), и преобразует pc.exe в pc.com (ключ –c). Отладчик, не найдя в исполняемом файле отладочной информации, ищет ее в файле с расширением tds.
