Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методички по ассемблеру / Методичка_часть2.doc
Скачиваний:
602
Добавлен:
02.05.2014
Размер:
545.28 Кб
Скачать

4.11. Командные параметры и переменные среды

В случае если команда не передавалась бы интерпретатору DOS, а выполнялась нами самостоятельно, то оказалось бы: чтобы запустить любую программу из-под shell.com, потребовалось бы предварительно переходить в каталог с этой программой или вводить ее с полным путем. Дело в том, что COMMAND.COM при запуске файла ищет его по очереди в каждом из каталогов, указанных в переменной среды PATH. DOS создает копию всех переменных среды (так называемое окружение DOS) для каждого запускаемого процесса. Сегментный адрес копии окружения для текущего процесса располагается в PSP по смещению 2Ch. В этом сегменте записаны все переменные подряд в форме ASCIZ-строк вида "COMSPEC=C:/WINDOWS/COMMAND.COM",0. По окончании последней строки стоит дополнительный нулевой байт, затем слово (обычно 1) — количество дополнительных строк окружения, а потом — дополнительные строки. Первая дополнительная строка — всегда полный путь и имя текущей программы — также в форме ASCIZ-строки. При запуске новой программы с помощью функции 4Bh можно создать полностью новое окружение и передать его сегментный адрес запускаемой программе в блоке ЕРВ или просто указать 0, позволив DOS скопировать окружение текущей программы.

Кроме того, в предыдущем примере мы передавали запускаемой программе (command.com) параметры (/с команда), но пока не объяснили, как программа может определить, что за параметры были переданы ей при старте. При запуске программы DOS помещает всю командную строку (включая последний символ 0Dh) в блок PSP запущенной программы по смещению 81h и ее длину в байт 80h (таким образом, длина командной строки не может быть больше 7Eh (126) символов). Под Windows 95 и 4DOS, если командная строка превышает эти размеры, байт PSP:0080h (длина) устанавливается в 7Fh, в последний байт PSP (PSP:00FFh) записывается 0Dh, первые 126 байт командной строки размещаются в PSP, а вся строка целиком — в переменной среды CMDLINE.

; cat.asm

; копирует объединенное содержимое всех файлов, указанных в командной строке,

; в стандартный вывод. Можно как указывать список файлов, так и использовать

; маски (символы "*" и "?") в одном или нескольких параметрах,

; например:

; cat header *.txt footer > all-texts помещает содержимое файла

; header, всех файлов с расширением .txt в текущем каталоге и файла

; footer - в файл all-texts

; длинные имена файлов не используются, ошибки игнорируются

;

.model tiny

.code

org 80h ; по смещению 80h от начала PSP находятся:

cmd_length db ? ; длина командной строки

cmd_line db ? ; и сама командная строка

org 100h ; начало СОМ-программы - 100h от начала PSP

start:

cld ; для команд строковой обработки

mov bp,sp ; сохранить текущую вершину стека в ВР

mov cl,cmd_length

cmp cl,1 ; если командная строка пуста -

jle show_usage ; вывести информацию о программе и выйти

mov ah,1Ah ; функция DOS 1Ah

mov dx,offset DTA

int 21h ; переместить DTA (по умолчанию она совпадает

; с командной строкой PSP)

; преобразовать список параметров в PSP:81h следующим образом:

; каждый параметр заканчивается нулем (ASCIZ-строка)

; адреса всех параметров помещаются в стек в порядке обнаружения

; в переменную argc записывается число параметров

mov cx,-1 ; для команд работы со строками

mov di,offset cmd_line ; начало командной строки в ES:DI

find_param:

mov al,' ' ; искать первый символ,

repz scasb ; не являющийся пробелом

dec di ; DI - адрес начала очередного параметра

push di ; поместить его в стек

inc word ptr argc ; и увеличить argc на один

mov si,di ; SI = DI для следующей команды lodsb

scan_params:

lodsb ; прочитать следующий символ из параметра,

cmp al,0Dh ; если это 0Dh - это был последний параметр

je params_ended ; и он кончился,

cmp al,20h ; если это 20h - этот параметр кончился,

jne scan_params ; но могут быть еще

dec si ; SI - первый байт после конца параметра

mov byte ptr [si],0 ; записать в него 0

mov di,si ; DI = SI для команды scasb

inc di ; DI - следующий после нуля символ

jmp short find_param ; продолжить разбор командной строки

params_ended:

dec si ; SI - первый байт после конца последнего

mov byte ptr [si],0 ; параметра - записать в него 0

; каждый параметр воспринимается как файл или маска для поиска файлов,

; все найденные файлы выводятся на stdout. Если параметр - не имя файла,

; то ошибка игнорируется

mov si,word ptr argc ; SI - число оставшихся параметров

next_file_from_param:

dec bp

dec bp ; BP - адрес следующего адреса параметра

dec si ; уменьшить число оставшихся параметров,

js no_more_params ; если оно стало отрицательным - все

mov dx,word ptr [bp] ; DS:DX - адрес очередного параметра

mov ah,4Eh ; Функция DOS 4Eh

mov cx,0100111b ; искать все файлы, кроме каталогов

; и меток тома

int 21h ; найти первый файл,

jc next_file_from_param ; если произошла ошибка - файла нет

call output_found ; вывести найденный файл на stdout

find_next:

mov ah,4Fh ; Функция DOS 4Fh

mov dx,offset DTA ; адрес нашей области DTA

int 21h ; найти следующий файл,

jc next_file_from_param ; если ошибка - файлы кончились

call output_found ; вывести найденный файл на stdout

jmp short find_next ; продолжить поиск файлов

no_more_params:

mov ax,word ptr argc

shl ax,1

add sp,ax ; удалить из стека 2 * argc байт

; (то есть весь список адресов

; параметров командной строки)

ret ; конец программы

; процедура show_usage

; выводит информацию о программе

show_usage:

mov ah,9 ; Функция DOS 09h

mov dx,offset usage

int 21h ; вывести строку на экран

ret ; выход из процедуры

; процедура output_found

; выводит в stdout файл, имя которого находится в области DTA

output_found:

mov dx,offset DTA+1Eh ; адрес ASCIZ-строки с именем файла

mov ax,3D00h ; Функция DOS 3Dh

int 21h ; открыть файл (al = 0 - только на чтение),

jc skip_file ; если ошибка - не трогать этот файл

mov bx,ax ; идентификатор файла - в ВХ

mov di,1 ; DI будет хранить идентификатор STDOUT

do_output:

mov cx,1024 ; размер блока для чтения файла

mov dx,offset DTA+45 ; буфер для чтения/записи располагается

; за концом DTA

mov ah,3Fh ; Функция DOS 3Fh

int 21h ; прочитать 1024 из файла,

jc file_done ; если ошибка - закрыть файл

mov cx,ax ; число реально прочитанных байт в СХ,

jcxz file_done ; если это не ноль - закрыть файл

mov ah,40h ; Функция DOS 40h

xchg bx,di ; BX = 1 - устройство STDOUT

int 21h ; вывод прочитанного числа байт в STDOUT

xchg di,bx ; вернуть идентификатор файла в ВХ,

jc file_done ; если ошибка - закрыть файл

jmp short do_output ; продолжить вывод файла

file_done:

mov ah,3Eh ; Функция DOS 3Eh

int 21h ; закрыть файл

skip_file:

ret ; конец процедуры output_found

usage db "cat.com v1.0",0Dh,0Ah

db "объединяет и выводит файлы на stdout",0Dh,0Ah

db "использование: cat имя_файла, ...",0Dh,0Ah

db "(имя файла может содержать маски * и ?)",0Dh,0Ah,'$'

argc dw 0 ; число параметров (должен быть 0 при старте программы!)

DTA: ; область DTA начинается сразу за концом файла,

; а сразу за областью DTA начинается

; 1024-байтный буфер для чтения файла

end start

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