Вивід вмісту каталогу y середовищі MS DOS

зміст

	

	вступ	3

	1. Постановка задачі	4

	2. Програмний інструментарій	5

	2.1. Вибір засобу виконання поставленої задачі.	5

	2.2. Функції переривання INT 21h MS DOS, що використані при роботі програм	5

	2.2.1. Функції роботи із DTA	5

	2.2.2. Інші функції переривання INT 21h, що необхідні для використання у програмі, що розробляється	7

	3. Розробка задачі на мові асемблер	10

	3.1. Допоміжні процедури	10

	3.1.1. Модуль PARAMS.asm	10

	3.1.2. Модуль STRIO.asm	14

	3.1.3. Модуль BINASC.asm	17

	3.2. Розробка основної програми DR.asm	20

	4. Розробка задачі на мові високого рівня із використанням асемблерних фрагментів коду	30

	4.1. Розробка програми на Pascal	30

	Висновки	34

	Список використаних джерел	35

	Додатки	36

вступ



Еволюція мов програмування нараховує вже не один десяток років. Пройдено великий етап від мов структурного програмування (таких як С) до систем багатомовного програмування, на роль яких претендують такі бренди як С#, Java, та ін. Однак слід зазначити, що незважаючи на постійне оновлення мов високого рівня щодо їх пристосування до зростаючих потреб програмістів, головна їх задача лишається тією ж самою – транслювати свої команди у машинний код. Тобто, не має ніякої різниці, якою мовою виконувати задачу – результат лишається той же; відмінність полягає лише у тому, як ця мова інтерпретує рішення програміста у мову машинних кодів. Тому відповідь на питання – чому мова асемблера, залишаючись практично незмінною у своїй ідеології і лексичній базі (цього не можна сказати, наприклад, про сімейство С), лишається й досі конкурентноспроможною іншим мовам – є цілком зрозумілою: вона, як мова програмування низького рівня, найближча до мови комп'ютера і, наразі, найбільш ефективна. 

Мета даного курсового проекту – відбити ідеологію мови асемблера і продемонструвати можливості, які надає їй взаємодія із середовищем MS DOS, на прикладі розробки програми перегляду каталогів і виводу інформації про файли, що в них містяться.

Однак, не зважаючи на всі переваги мови асемблера, вона має вагомий недолік відносно мов високого рівня, що й зумовлює їх існування. У порівнянні з асемблером вони значно спрощують роботу програміста шляхом реалізації різноманітних аспектів алгоритму, таких як вивід на екран інформації, реалізація циклів, тощо за допомогою власних вбудованих інструментів. 

Наразі, друга частина цього проекту присвячена розробці програми, що виконує аналогічні задачі вже мовою Pascal, надаючи реалізацію основного алгоритму асемблерним вставкам, а елементів інтерфейсу – функціям Pascal.

Таким чином, даний курсовий проект має продемонструвати переваги і недоліки програмування мовами низького і високого рівня, а також розкрити деякі аспекти роботи середовища MS DOS і його функцій.



1. Постановка задачі



Згідно із темою даного курсового проекту його задача – показати яким чином можна в середовищі MS DOS реалізувати вивід на екран всіх даних по файлам і директоріям, що містяться в заданій директорії, а також розробити програму, що реалізує ці дії. Оскільки робота відбувається у середовищі MS DOS, цілком очевидно, що можна використовувати всі інструменти і можливості, що надає це середовище програмісту.

Програма має бути виконана мовою асемблера, а також можливий варіант створення програми на мові високого рівня (наприклад Pascal, C та ін.) із використанням фрагментів асемблерного коду, що виконують основний алгоритм задачі.

Програми мають містити коментарі і схеми алгоритмів.


2. Програмний інструментарій



2.1. Вибір засобу виконання поставленої задачі



Існує два шляхи вирішення поставленої задачі. Розглянемо кожний з них окремо.

Перший засіб

Послідовне переміщення по дереву каталогу шляхом безпосереднього читання (за допомогою другої функції переривання INT 13h BIOS) секторів пам'яті, починаючи із ROOT, і пошуку необхідного підкаталогу згідно із вказаним шляхом до потрапляння у кінцевий каталог із виводом даних о всіх файлах і директоріях, що в ньому містяться [1, 2].

Однак даний метод надзвичайно складний, має прив'язку до певної системи FAT, організації BOOT і таблиці розділів, потребує прямого звертання до жорсткого диску, що не свідчить в його користь.

Другий засіб

Пов'язаний із специфікою роботи MS DOS. Коли програма починає виконуватись, регістри DS:0000 і ES:0000 вказують на початок PSP (Program Segment Prefix – префікс програмного сегмента) – об'єм пам'яті, в якому міститься інформація о розмірі доступної пам'яті, опції із рядка команд, імена файлів, оточення, а також DTA (розмір 43 байти) [4, 6].

DTA має наступну структуру:

		Резервна область.

		Атрибут.

		Час створення файлу.

		Розмір файлу.

		Ім'я із розширенням файлу.

Таким чином DTA дає повну і вичерпну інформацію о файлі. Тобто, читаючи послідовно DTA кожного файлу директорії, можна отримати інформацію по кожному файлу і вивести її на екран.

Пошук DTA файлів реалізується за допомогою спеціальних функцій переривання INT 21h MS DOS. Для виводу інформації на екран, і завершення програми, теж використовуються спеціальні функції переривання INT 21h.



2.2. Функції переривання INT 21h MS DOS, що використані при роботі програм



2.2.1. Функції роботи із DTA

Середовище MS DOS пропонує набір функцій переривання INT 21h, що дозволяють працювати із DTA – встановити його адресу (1Ah), дати поточний DTA (2Fh), заповнити DTA при знаходженні першого файлу (4Eh), заповнити DTA при знаходженні наступного файлу (4Fh). Розглянемо кожну з цих функцій окремо:

1Ah функція INT 21h [6]

Вхід:		AH 1Ah

DS:DX адреса для DTA

Вихід:	не має

Дана функція встановлює адресу для DTA. В регістрову пару DS:DX заноситься, відповідно, сегмент і зміщення буфера розміром >43 байтів, в який буде зберігатись DTA. Якщо адреса DTA змінюється вашою програмою, бажано зберегти попередню адресу DTA (за допомогою функції 2Fh INT 21h) і відновити її після завершення програми.

Приклад:

mov ah, 1Ah		; в ah номер функції 

mov dx, offset buffer	; в dx поміщується зміщення буфера,

; сегмент лишається незмінний

int 21h

В даному прикладі перевизначається адреса для буфера DTA.

2Fh функція INT 21h [6]

Вхід: 		AH 2Fh

Вихід: 	ES:BS адреса початку поточної DTA.

Функція повертає адресу початку області вводу/виводу DTA. В AH заноситься номер функції; в регістровій парі ES:BS повертається адреса початку DTA. 

Дана функція корисна при збереженні адреси DTA для її подальшого відновлення після зміни програмою. Слід бути обережним при її використанні, оскільки функція змінює регістр es.

Приклад:

push es 		; збереження у стеку регістрів es, bx, що зміняться

push bx		

mov ah, 2Fh		;в ах номер функції

int 21h

mov [DTAs], es	; збереження адреси DTA

mov [DTAo], bx

pop bx		; відновлення попередніх значень регістрів bx і es

pop es		; із стеку

В даному прикладі у змінних DTAs і DTAo зберігається адреса DTA, регістри bx, es незмінні.

4Eh функція INT 21h [6]

Вхід: 		AH 4Fh

DS:DX адреса ASCII рядка із маскою імені файлу

СХ атрибут файлу для порівняння

Вихід: 	АХ код помилки, якщо в CF=1 буфер DTA заповнений даними.

Дана функція знаходить ім'я першого файлу у переліку, що відповідає заданій масці і атрибуту, і поміщує інформацію про нього в буфер DTA. В AH поміщується номер функції; регістрова пара вказує на рядок, що задає маску імені файлу; в СХ міститься атрибут файлу. 

В імені файлу і розширенні допускаються узагальнені символи "*" і "?".

Функція повертає код помилки в AX, якщо файл не знайдений, або заповнює буфер DTA даними про файл.

4Fh функція INT 21h [6]

Вхід: 		AH 4Fh

DS:DX адреса даних, повернутих попередньою 4Eh 

функцією INT 21h

Вихід: 	AX код помилки, якщо CF=1 

DTA заповнена даними

Дана функція викликається після функції 4Eh INT 21h і знаходить наступний файл, що відповідає параметрам пошуку, заданим при визові 4Eh INT 21h.

В AH заноситься номер функції, в регістрову пару DS:DX – адреса даних, повернутих попередньою функцією.

Функція повертає в AX код помилки, якщо вона не відбулась, або поміщує в буфер DTA дані про файл, що відповідає узагальненому імені і атрибуту.

Приклад:

mov ah, 4Eh 		; в аh номер функції першого пошуку

mov dx, affset mask 	; в dx зміщення маски

mov cx, 10h

int 21h

jc end

@@01:

mov ah, 4Fh 		; в аh номер функції наступного пошуку

int 21h

jnc @@01

end:

Приклад демонструє перегляд каталогу на наявність файлів, що відповідають масці, заданої змінною mask. В буфері DTA залишається опис останнього знайденого файлу.



2.2.2. Інші функції переривання INT 21h, що необхідні для використання у програмі, що розробляється

2h функція INT 21h [6]

Вхід: 		AH 02h

DL	код символу, що виводиться на вивід

Вихід:	символ на екрані

В AH поміщується код функції, в DL код символу, що необхідно вивести на екран.

Слід відмітити, що при наявності в DL коду 8 (ASCII код Backspace) функція переміщує курсор вліво на одну позицію.

Для виконання переходу на новий рядок слід послідовно вивести символи із кодами 13 і 10 на екран [5].

Приклад:

mov 	ah, 2 		; номер функції

mov 	dl, 13 		; вертикальна табуляція

int 	21h

mov 	dl, 10 		; зсув каретки

int 	21h

Даний фрагмент коду переводить курсор на новий рядок.

40h функція INT 21h [6]

Вхід: 		AH 		40h

BX 		описувач файлу 

DS:DX 	адреса буфера, що містить дані, що записуються

CX 		число байт, що записуються

Вихід: 	AX 		код помилки, якщо CF=1

AL 		число реально зчитаних байт

Дана функція записує CX байт даних в файл, або пристрій, заданий описувачем в BX. В AH поміщується номер функції, BX містить описувач [6]:

0	Стандартний пристрій вводу (звичайна клавіатура)

1	Стандартний пристрій виводу (звичайний екран)

2	Стандартний пристрій помилок (CON-екран)

3	Стандартний пристрій AUX (COM 1)

4	Стандартний принтер (LPT1)

Регістрова пара DS:DX адресується на буфер. В CX міститься кількість записуваних байт.

Функція повертає в AX код помилки при умові, що CF встановився в 1, або кількість реально зчитаних байтів в AL.

Для нас дана функція цікава як інструмент для виводу даних на екран, тому в BX має бути занесено 1.

Приклад:

mov	ah, 40h 		; код функції

mov	bx, 1			; вивід на екран

mov	dx, offset sstring 	; в dx зміщення рядка виводу

mov	cx, FFh		; в cx кількість символів, що виводяться

int	21h

Даний фрагмент коду виводить на екран рядок символів, що містяться в змінній sstring.

4Ch функція INT 21h [6]

Вхід: 		AH	4Ch

AL 	код виходу

Вихід: 	не має

Функція завершення програми (EXIT). Повертає управління від породженого процесу його батьківському процесу. Встановлює код виходу (його можна опитати функцією WAIT (4Dh)).

В AХ міститься номер функції, в AL – код виходу:

0 		нормальне завершення

1		завершення через Ctrl-Break (INT 23h)

2		завершення по критичній помилці пристрою (INT 24h)

3		завершення через функцію KEEP (31h)

Приклад:

mov	ax, 04ch	; в al – код виходу

int 	21h 		; в ah – номер функції

Даний фрагмент коду задає нормальне завершення роботи програми (повертається код виходу – 0).

3. Розробка задачі на мові асемблер



3.1. Допоміжні процедури



Очевидно, що основна програма потребує допоміжні процедури для отримання необхідних параметрів, які задає користувач в командному рядку при визові програми. Серед них: процедури вводу/виводу даних на консоль, обробки ASCII рядків, а також перетворення числових даних у ASCII формат для подальшого їх виведення на екран. Для цього були розроблені спеціальні модулі PARAMS.asm, STRIO.asm та BINASC.asm, які містять необхідні процедури. Розглянемо їх окремо.



3.1.1. Модуль PARAMS.asm

Традиційно, програми MS DOS дозволяють користувачу вводити в командному рядку одне чи більше імен файлів і інші дані. Для нас це цікаве можливістю одразу при визові основної програми DR.exe задавати шлях до директорії та маску файлів, які ми бажаємо вивести на екран [7].

Наприклад:

c > DR c:\windows\*.sys

Тобто даний ввід має викликати програму DR.exe, яка виведе усі файли із розширенням .sys, які знаходяться за адресою c:\windows. Мова асемблера не дає нам вбудованих механізмів реалізації даної можливості, тому виникає необхідність розробки власного програмного модуля для роботи із командним рядком.

При завантаженні exe-файлу command.com створює в пам'яті PSP блок (256 байт), у якому, серед іншої інформації, містить текст, який йде після імені програми (хвіст команди). Перед початком виконання програми адреса PSP міститься в регістровій парі ds:es. Хвіст команди починається зі зміщення 80h (до FFh) і займає 128 байт, при чому перший символ знаходиться за зміщенням 81h, а в 80h міститься кількість символів хвоста команди [4, 6].

Ідея модуля PARAMS.asm в тому, що створюється власний 128-ми байтовий буфер, в який (за допомогою функції GetParams) копіюється хвіст команди, а потім виконується обробка отриманих даних за допомогою функції GetOneParam (отримання адреси параметра за номером) і ParamCount (отримання кількості параметрів).

Параметри в хвості команди розділені пробілами, останній символ – символ повернення каретки.

На основі сказаного було розроблено наступний програмний модуль:

IDEAL



MODEL small



TailLen		EQU 0080h		; зміщення байта із довжиною рядка 

; параметрів

CommandTail	EQU 0081h		; зміщення першого символу рядка

; параметрів



DATASEG



numParams 		DW ? 		; кількість параметрів

params		DB 128 DUP (?)	; буфер на 128 байт для хвоста команди



CODESEG



PUBLIC ParamCount, GetParams, GetOneParam

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

; Separators 		внутрішня процедура для перевірки на пробіли, табуляцію, 

; 			повернення каретки

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

; Вхід 	ds:si 		адреса символу, що перевіряється

; Вихід 	ZF=1 		символ є пробілом, табуляцією чи поверненням каретки

ZF=1 		символ не є роздільником

; Регістри 			не змінюються

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

PROC Separators

push	ax				; збереження у стеку ax

mov	al, [si]			; в al поміщується символ із ds:si

cmp	al, 020h			; порівняння al із пробілом

je	@@10			; якщо так, то перехід

cmp	al, 009h			; порівняння al із табуляцією

je	@@10			; якщо так, то перехід

cmp	al, 00Dh			; порівняння al із символом повернення

; каретки

@@10:

pop ax				; відновлення ax

ret					; повернення до викликаючої програми

ENDP Separators

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

; ParamCount 			повертає кількість параметрів у хвості команди

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

; Вхід 				не має

; Вихід		CX		кількість параметрів командного рядка

; Регістри		CX

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

PROC ParamCount

mov	cx, [numParams]	; отримати значення змінної numParams

ret				; повернення до викликаючої програми

ENDP ParamCount

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

; GetParams		занесення параметрів командного рядка DOS у буфер

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

; Вхід 	ds	префікс сегмента програми (PSP) (адресує PSP, якщо його

; 			не змінювали)

; 		es	сегмент даних програми

; Вихід	[params]	початок буфера заповненого даними

; 		[numParams] 	кількість параметрів

; 		ds 	сегмент даних програми

; Регістри	al, bx, dx, si, di, ds

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

PROC GetParams

;------Ініціалізація cx і індексних регістрів si і di

push	ax 				; збереження регістрів

push bx

push dx

push si

push di

xor	ch, ch				; обнуління верхньої половини cx

mov	cl, [ds:TailLen]		; в cx довжина параметрів

inc	cx				; включити символ повернення каретки

mov	si, CommandTail		; адреса параметрів поміщується в si

mov	di, offset params		; адреса призначення поміщується в di

; ------Пропуск початкових пробілів і табуляції

@@10:

call	Separators			; пропуск пробілів і табуляції

jne	@@20			; перехід, якщо пробілів і табуляції не має

inc	si				; пропуск символу

loop	@@10			; цикл, доки не скінчиться обробка, або cx=0

; ------Копіювання параметрів рядка в буфер params

@@20:

push	cx				; збереження cx у стеку

jcxz	@@30			; пропуск копіювання, якщо cx=0

cld					; збільшення на 1 si і di

rep	movsb			; копіювання cx байтів із ds:si в es:di

; ------Перетворення пробілів в 0 і встановлення numParams

@@30:

push	es

pop	ds				; ds = es

pop	cx				; відновлення cx (довжину)

xor	bx, bx				; обнуління bx, лічильник параметрів

jcxz	@@60			; пропуск циклу якщо cx=0 (довжина)

mov	si, offset params		; поміщення адреси параметрів в si

@@40:

call	Separators			; перевірка на пробіли, табуляцію,

					; повернення каретки

jne	@@50			; перехід, якщо не знайдено роздільник

mov	[byte ptr si], 0		; заміна роздільника на 0

inc	bx				; збільшення лічильника кількості 

; параметрів

@@50:

inc	si				; переміщення указника на наступний

; символ

loop	@@40			; виконувати в циклі, доки cx ≠ 0

@@60:

mov	[numParams], bx		; збереження в numParams кількість 

; параметрів

pop	ax				; відновлення регістрів

pop bx

pop dx

pop si

pop di

ret					; повернення до батьківської програми

ENDP GetParams

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

; GetOneParam	отримати адресу параметра за номером

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

; Вхід		cx	номер параметра (має бути менше значення в numParams)

; Вихід	di	зміщення ASCII рядка із потрібним параметром

; Регістри	di	

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

PROC GetOneParam

push	ax				; збереження регістрів ax і cx

push	cx

xor	al, al				; обнуління al (ініціалізація шуканого

; значення 0)

mov	di, offset params		; адреса параметрів рядка

jcxz @@99				; якщо номер параметра (cx) дорівнює 0,

; то вихід

cmp	cx, [numParams]		; порівняння cx із кількістю параметрів

jae	@@99			; вихід, якщо передано неіснуючого 

; параметру

cld					; автоматичне збільшення di

@@10:

scasb					; пошук нульового обмежувача

jnz	@@10			; повтор, доки не знайдено 0

loop	@@10			; повтор, доки в cx не буде 0

@@99:

pop	cx				; відновлення регістрів cx, ax

pop	ax

ret					; повернення до викликаючої програми

ENDP GetOneParam

END

Таким чином, програмний модуль PARAMS.asm є зручним інструментом для реалізації роботи із командним рядком і буде використаний в основній програмі.



3.1.2. Модуль STRIO.asm

Оскільки важливою частиною основної програми, згідно із завданням, буде вивід текстових рядків на екран, то є необхідність у створенні спеціального програмного модуля, який би містив процедури для обробки і виводу ASCII рядків на екран. Пряме використання функцій DOS в основній програмі є незручним, оскільки є потреба у спрощенні коду для його сприйняття. 

З цих міркувань було розроблено програмний модуль STRIO.asm, в якому міститься п’ять спеціальних функцій: StrLength (визначає кількість символів, записаних в ASCII рядку), дві функції виводу ASCII-рядків на екран – StrWrite і StrWrite2, а також функцію NewLine (перехід на новий рядок) та WriteSimv (виводить на екран заданий символ необхідну кількість разів).

Слід зазначити, що даний програмний модуль не містить функцій читання із консолі в рядок, однак основна програма отримує дані із PSP DOS-а і опрацьовує вже створені дані, а тому не потребує якихось додаткових вказівок через консоль від користувача, всі необхідні специфічні дані (наприклад, маска файлів) користувач може задати в командному рядку при визові основної програми.

Код програмного модуля STRIO.ASM приведений нижче:

IDEAL



MODEL small



ASCnull	EQU		0		; ASCII нуль

ASCcr		EQU		13	; ASCII символ повернення каретки

ASClf		EQU		10	; ASCII символ вертикальної табуляції

; (прогону рядка)



CODESEG



PUBLIC StrLength, StrWrite, StrWrite2, NewLine, WriteSimv

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

; StrLength 		підраховує кількість ненульових символів в рядку

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

; Вхід		di	адреса ASCII рядка

; Вихід 	cx	кількість ненульових символів в рядку

; Регістри	cx	

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

PROC StrLength

push	ax				; зберегти у стеку змінювані

push 	di 				; регістри ax, di

xor 	al, al				; в al поміщується шуканий символ 0

mov 	cx, 0ffffh			; в cx максимальна глибина пошуку

cld					; автоматичне збільшення di

repnz	 scasb				; шукати al, доки [di] або cx не стане 0

not 	cx				; логічне заперечення cx

dec 	cx				; зменшення cx на 1 – довжина рядка

pop 	di				; відновлення регістрів

pop 	ax

ret					; повернення до викликаючої програми

ENDP StrLength

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

; StrWrite 		вивід рядка на стандартний пристрій виводу

; StrWrite2		вивід заданої кількості символів рядка на консоль

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

; Вхід		di	адреса ASCII рядка

; 		cx	кількість записуваних символів (для StrWrite2)

; Вихід		символьний рядок виводиться на стандартний пристрій

;			виводу

Регістри	cx	(для StrWrite)

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

PROC StrWrite

call	StrLength			; встановити в cx довжину рядка

PROC StrWrite2				; друга змінна точка входу

push	ax				; збереження змінюваних регістрів

push	bx

push	dx

mov	bx, 1				; задання стандартного пристрою виводу

mov	dx, di				; адресація ASCII рядка в ds:dx

mov	ah, 40h			; в ax номер функції, що виконує запис 

; в файл або на пристрій виводу

int	21h				; виклик 21 переривання DOS

pop	dx				; відновлення збережених регістрів 

pop	bx				; із стеку 

pop	ax

ret					; повернення до визиваючої програми

ENDP StrWrite2

ENDP StrWrite

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

; NewLine		перейти на новий рядок на стандартному пристрої виводу

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

; Вхід 		не має

; Вихід		на пристрій виводу посилаються символи повернення 

; 			каретки і прогону рядка

; Регістри		не має

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

PROC NewLine

push	ax				; збереження регістрів у стек

push	dx

mov	ah, 2				; в ah номер функції виводу символу у DOS

mov	dl, ASCcr			; в dl символ повернення каретки

int	21h				; вивести символ повернення каретки

mov	dl, ASClf			; в dl символ прогону рядку

int	21h				; вивести символ прогону рядку

pop	dx				; відновлення регістрів із стеку

pop	ax

ret					; повернення до викликаючої програми

ENDP NewLine

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

; WriteSimv		вивід на стандартний пристрій виводу заданий символ

; 			визначену кількість разів

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

; Вхід		dl	код символу

; 		cx	кількість виводів символу

; Вихід 		на пристрій виводу задану кількість разів посилається 

; 			переданий символ

;Регістри		не має

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

PROC WriteSimv

push	ax				; збереження регістрів

push	cx

@@01:

mov	ah,02				; в ah номер функції DOS запису символу 

int	21h				; вивести заданий символ

loop 	@@01			; повторювати доки cx≠0

pop	cx				; відновлення регістрів

pop	ax

ret					; повернення до викликаючої програми

ENDP WriteSimv

END

Функції, надані програмним модулем STRIO.asm, є зручними і простими інструментами виводу інформації на стандартний пристрій виводу і будуть використані в основній програмі.



3.1.3. Модуль BINASC.asm

Мови високого рівня надають програмісту можливість безпосередньо зчитувати і виводити числові значення. Нажаль, мова асемблера таких інструментів не має. В основній програмі значна частина роботи пов’язана з виводом із деякого буфера даних на екран . Однак дані в буфері зберігаються у вигляді двійкових слів того чи іншого типу. З'являється необхідність перетворення двійкових даних у ASCII-рядки, щоб у подальшому їх можна було вивести на екран. Дану проблему і покликані вирішити функції модуля BINASC.asm. Модуль складається із чотирьох функцій: допоміжних функцій HexDigit (перетворення чотирьохбітового значення у ASCII-цифру) і NumToAscii (перетворення беззнакового двійкового числа у ASCII-рядок), а також двох функцій BinToAscHex і BinToAscDec, які встановлюють систему числення і викликають вищезгадані функції.

Слід зазначити, що функція BinToAscDec зручна для перетворення і подальшого виводу чисел типу "слово" у вигляді десяткового числа.

Функцію BinToAscHex можна використовувати для виводу подвійного слова у вигляді шістнадцятирічного числа, послідовно перетворюючи і виводячи спочатку молодші два, а потім і старші байти, на екран.

Це дозволяє вирішити проблему обробки чотирьохбайтових даних, оскільки звичайні регістри є двохбайтовими і перетворення такого числа у, наприклад, десяткове представлення є проблематичним.

Код програмного модуля BINASC.asm приведено нижче:

IDEAL



MODEL small



ASCnull	EQU		0		; нульовий ASCII-символ



DATASEG



CODESEG

PUBLIC		HexDigit, NumToAscii

PUBLIC		BinToAscHex, BinToAscDec

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

; HexDigit		перетворює чотирьохбітове значення в ASCII-цифру

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

; Вхід		dl	 значення від 0 до 15

; Вихід	dl	шістнадцятирічний еквівалент ASCII-цифри

; Регістри	dl	

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

PROC HexDigit

cmp	dl, 10				; перевірка, чи є dl < 10 

; (тобто менше шістнадцятирічного 'А')

jb	@@10			; якщо так, то перехід

add	dl, 'A'-10			; перетворити в A, B, C, D, E або F

ret					; повернення до викликаючої програми

@@10:

or	dl, '0'				; перетворити в числа від 0 до 9

ret					; повернення до викликаючої програми 

ENDP HexDigit

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

; NumToAscii	перетворює беззнакове двійкове значення у ASCII-рядок 

; 			згідно із заданою системою числення

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

; Вхід		ax	двохбайтове число, яке перетворюється 

; 		bx	основа системи числення результату (2 – двійкова, 

; 			10 – десяткова, 16 – шістнадцятирічна)

; 		cx	мінімальна кількість цифр, що виводяться

; 		di	адреса рядка для результату

; Вихід	di	вказує на новостворений рядок із результатом

; Регістри		не має

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

PROC NumToASCII

push	dx				; збереження змінюваних регістрів

push	di

push	si

xor	si, si				; встановити лічильник цифр у стеку в 0 

jcxz	@@20			; якщо cx = 0, то перехід

@@10:

xor	dx, dx				; обнуління dx; ax розширюється до 

; 32-х-бітного dxax 

div	bx				; в ax результат ділення на bx, в dx залишок

call	HexDigit			; перетворення числа в dl в ASCII-пару

push	dx				; збереження цифри в стеку

inc	si				; збільшення лічильника цифр у стеку

loop	@@10			; виконувати цикл, доки не оброблена 

; мінімальна кількість цифр

@@20:

inc	cx				; встановити cx=1, якщо не усі цифри 

; оброблені

or	ax, ax				; перевірка ax на обробку всіх цифр

jnz	@@10			; якщо ax≠a, продовження перетворень

mov	cx, si				; в cx поміщується кількість цифр у стеку

jcxz	@@40			; пропуск наступного циклу, якщо cx=0

cld					; автоматичне збільшення di

@@30:

pop	ax				; в ax поміщується цифра із стеку

stosb					; запис цифри в рядок і збільшення di

loop	@@30			; в циклі вивід cx цифр

@@40:

mov	[byte di], ASCnull		; записується 0 у кінець рядка

pop	si				; відновлення регістрів

pop	di

pop	dx

ret					; повернення до викликаючої програми

ENDP NumToASCII

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

; BinTo AscHex	перетворює двійкове значення в шістнадцятирічні 

; 			ASCII-рядки 

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

; Вхід		ax 	двохбайтове значення, що перетворюється

; 		cx	мінімальна кількість чисел, що виводиться

; 		di	адреса рядка для результату

; Вихід	di	вказує на рядок із сформованим результатом

; Регістри		не має

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

PROC BinToAscHex

mov	bx, 16				; в bx встановити основу шістнадцятирічної 

; системи числення - 16

call	NumToAscii		; перетворення числа із ax в ASCII-рядок, 

; на який вказує di

ret					; повернення до викликаючої програми

ENDP BinToAscHex

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

; BinTo AscHex	перетворює двійкове значення в десяткові ASCII-рядки

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

; Вхід		ax 	двохбайтове значення, що перетворюється

; 		cx	мінімальна кількість чисел, що виводиться

; 		di	адреса рядка для результату

; Вихід	di	вказує на рядок із сформованим результатом

; Регістри		не має

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

PROC BinToAscDec

mov	bx, 10				; в bx встановити основу десяткової 

; системи числення – 10

call	NumToAscii		; перетворення числа із ax в ASCII-рядок, 

; на який вказує di

ret					; повернення до викликаючої програми 

ENDP BinToAscDec			

END

Таким чином програмний модуль BINASC.asm дає нам спеціальні функції, що дозволяють перетворити і вивести на екран дані із DTA, що описують файли.



3.2. Розробка основної програми DR.asm



Модулі PARAMS.asm, STRIO.asm і BINASC.asm складають функціональну базу програмних інструментів для розробки основної програми. Згідно із поставленою задачею, програма має знаходити файли, задані маскою, копіювати DTA, що їх описує, у власний буфер, обробляти отримані дані і виводити необхідну інформацію на екран, а потім переходити до наступного файлу, що відповідає масці, доки не обробить всі.

Таким чином задачу можна розбити на 3 частини:

Отримання конфігураційних даних із консолі і, при їх відсутності, встановлення стандартної маски файлів;

Пошук файлів, що відповідають масці, і заповнення внутрішнього буферу їх DTA (процедура DirEgine);

Саме обробка DTA, вивід даних на екран (процедура Action).

Спираючись на викладені міркування, було створено основну програму DR.asm:

IDEAL



MODEL small

STACK 256



FileName EQU 30				; зміщення імені файлу в буфері dirData



DATASEG



exCode		DB	0		; код виходу



defaultSpec	DB		'*.*', 0	; стандартній ASCII-шаблон маски

DTAseg		DW	?		; сегмент для DTA 

DTAofs		DW	?		; зміщення для DTA 



dirData		DB	43 DUP (?)	; буфер для запису вмісту каталогу

buffer			DB	6 DUP (?)	; буфер для збереження проміжних 

; ASCII-рядків

point			DB	' * ',0		; ASCII-шаблон зірочки



tit1 			DB	'The DIRWUER wersion 1.0',10,13, 'Romanov Alexander Urievich. KIT-13A NTU"KhPI"',10,13,'Copyright (C) 2005 by Romanov Alexander',0				; інформація о програмі

tabl			DB	'Filename	OnR Skr Sys Tom Kat Arh Time

Data			Size',0		; заголовок таблиці



CODESEG



EXTRN GetParams:Proc, GetOneParam:Proc, ParamCount:Proc

 із params.obj 



EXTRN StrLength:Proc, StrWrite:Proc

EXTRN NewLine:Proc, WriteSimv:Proc, StrWrite2:Proc 

; із strio.obj 



EXTRN BinToAscHex:Proc, BinToAscDec:Proc 

; із Binasc.obj 



Start:

mov	ax, @data			; встановлює в ax адресу сегмента даних

mov	es, ax				; встановлює в es адресу сегмента даних

; ------Отримання даних із командного рядка

call	GetParams			; отримати параметри із командного рядка 

; (ds=PSP)

call	NewLine			; перехід на новий рядок

; ------Виведення інформації о програмі

mov	di,offset tit1			; адреса рядка з інформацією о програмі

call	StrWrite			; вивід інформації о програмі на консоль

call	NewLine 			; перехід на новий рядок

; ------Вибір маски

call	ParamCount			; отримання в cx число параметрів

mov	di, offset defaultSpec	; встановити di вказівником на 

; стандартний шаблон маски

or	cx, cx				; перевірка cx на наявність 0

jz	@@10			; якщо cx=0, перехід

xor	cx, cx				; обнуління cx (номер параметру)

call	GetOneParam		; отримати адресу параметра

; ------Виклик підпрограми обробника

@@10:

mov	bx, offset Action		; поміщення в bx адреси процедури Action

	call	DirEngine			; виклик процедури DirEngine розгляду 

; каталогу

; ------Завершення роботи

Exit:

call	NewLine			; перехід на новий рядок

mov	ah, 04Ch			; в ah номер функції виходу із програми

mov	al, [exCode]			; в al код виходу

int	21h				; виклик DOS, завершення програми

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

; DirEngine			перегляд каталогу

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

; Вхід 	cx:bx		адреса підпрограми Action

;		ds:di		адреса ASCII-рядка для пошуку (маска)

; Вихід			викликає процедуру Action при кожному знаходженні 

; 				файла, що відповідає масці

Регістри	ax, cx, dl, di

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

PROC DirEngine

; ------Виведення заголовку таблиці

push	di				; збереження di у стеку

mov	di,offset tabl 		; в di зміщення рядка заголовка таблиці 

; ASCII

call	StrWrite			; вивід на екран заголовка таблиці

call	NewLine 

pop 	di 				; відновлення di

; ------Отримання поточного DTA

push	es				; збереження змінюваних регістрів

push	bx

mov	ah, 2Fh			; в ah номер функції DOS отримання DTA

int	21h				; отримати поточний DTA 

mov	[DTAseg], es		; збереження адреси сегмента DTA 

mov	[DTAofs], bx		; збереження адреси зміщення DTA 

pop	bx				; відновлення регістрів 

pop	es

; ------Встановлення нового DTA в глобальному 43-байтовому буфері dirData

mov	dx, offset dirData		; адреса змінної dirData поміщується 

; в ds:dx

mov	ah, 1Ah			; а ah номер функції DOS встановлення 

; DTA 

int	21h				; встановлення нового DTA

; ------Перевірка каталогу на співпадіння імен файлів із маскою в ds:dx

mov	ah, 4Eh			; в ah номер функції DOS першого 

; пошуку

mov	cx, 10h			; атрибут файлів і директорій 

mov	dx, di				; поміщення адреси рядка в ds:dx

jmp	short @@20		; пропуск наступної дії

@@10:

mov	ah, 4Fh			; в ah номер функції DOS продовження 

; пошуку

@@20:

int	21h				; пошук першого/наступного входження

jc	@@99			; вихід при помилці, або закінченні

call	bx				; виклик процедури Action

jmp	@@10			; повтор дій

@@99:

; ------Відновлення початкової адреси DTA

push	ds				; збереження ds у стеку

mov	ds, [DTAseg]		; встановлення старої адреси DTA в ds:dx

mov	dx, [DTAofs]

mov	ah, 1Ah			; в ah номер функції DOS встановлення 

; DTA 

int	21h				; повернення до старої DTA 

pop	ds				; відновлення ds

ret					; завершення процедури

ENDP DirEngine

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

; Action		виводить дані про файл із буфера dirData

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

; Вхід			dirData DTA файла

; Вихід		виводить дані про файл (назву, атрибути, дату/час створення,

; 			розмір)

; Регістри		ax, dl, di, cx

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

PROC Action 

	push	bx				; збереження регістрів

; ------Вивід імені файлу

mov	di, offset dirData + FileName 	; в di зміщення початку імені файлу

call	StrWrite			; вивід імені на екран

call	StrLength			; отримання в cx довжини імені

sub	cx, 15				; виявлення на скільки довжина імені 

neg	cx				; файлу менше поля із 15 символів

mov 	dl, ' '				; в dl символ пробілу

call	WriteSimv			; вивести на екран необхідну, для 

; доповнення поля із 15 символів, 

; кількість разів

; ------Вивід атрибутів файлу

mov	al,[offset dirData + FileName -9]; в al поміщується байт атрибуту

mov	cx,6				; в cx кількість значущих бітів у байті 

; атрибуту

@@01:

shr	al,1				; зсув al вправо,

; в CF поміщується молодший біт

jb	@@02			; якщо CF=1, то перехід

mov 	dl, ' '				; в dl поміщується пробіл (символ виводу)

push	cx				; збереження cx в стеку

mov	cx,4				; поміщення в cx кількості символів для

; виводу (4)

call	WriteSimv			; вивід 4-х пробілів

pop	cx				; відновлення cx

jmp	@@03			; перехід

@@02:

mov 	di,offset point		; в di поміщується зміщення змінної point

push	cx				; збереження cx

call	StrWrite			; вивід на екран зірочки

pop	cx				; відновлення cx

@@03:

loop @@01			; виконувати цикл, доки cx≠0 (6-ть разів)

; ------Вивід години створення файлу

mov bx,[offset dirData + FileName-8] ; в bx поміщується слово часу

; створення файлу

mov	ax,bx				; в ax поміщуються слово години 

; створення файлу

and 	ax,0f800h			; накладання маски

shr	ax,11				; зсув результату (отримали години)

mov 	cx,1

mov 	di,offset buffer 		; di вказує на пустий буфер buffer

call	BinToAscDec		; передача в змінну buffer 10-кового 

; ASCII-представлення числа з ax

mov	cx,2				; в cx ширина поля виводу

call	StrWrite2			; вивід ASCII-представлення числа з ax

mov	cx,1				; встановлення в cx кількості виводів

mov 	dl, ':'				; передача в dl символу, що виводиться

call WriteSimv			; вивід один раз символу із dl

; ------Вивід хвилин створення файлу

mov	ax,bx				; в ax поміщується слово хвилини 

; створення файлу

and 	ax,7e0h			; накладання маски

shr ax,5				; зсув результату (отримали хвилини)

mov 	cx,1

mov 	di,offset buffer		; di вказує на пустий буфер buffer

call	BinToAscDec		; передача в змінну buffer 10-кового 

; ASCII-представлення числа з ax

mov 	cx,2				; в cx ширина поля виводу

call	StrWrite2			; вивід ASCII-представлення числа з ax

mov	cx,1				; встановлення в cx кількості виводів

mov 	dl, ':'				; передача в dl символу, що виводиться

call WriteSimv			; вивід один раз символу із dl

; ------Вивід секунд створення файлу

mov	ax,bx				; в ax поміщується слово секунди

; створення файлу

and 	ax,1fh				; накладання маски

shl	ax,1				; помноження на 2 (отримання секунд із 

; двохсекундних одиниць)

mov 	cx,1

mov 	di,offset buffer		; di вказує на пустий буфер buffer

call	BinToAscDec		; передача в змінну buffer 10-кового 

; ASCII-представлення числа з ax

mov 	cx,3				; в cx ширина поля виводу

call	StrWrite2			; вивід ASCII представлення числа з ax

mov	cx,1				; встановлення в cx кількості виводів

mov 	dl, ' '				; передача в dl символу, що виводиться

call WriteSimv			; вивід один раз символу із dl

; ------Виведення дня створення файлу

mov bx,[offset dirData + FileName-6] ; в bx слово дати створення 

; файлу

mov	ax,bx				; в ax слово дня створення файлу

and 	ax,01fh			; накладання маски

mov 	cx,1				; встановлення мінімальної довжини 

; перетворюваного рядка

mov 	di,offset buffer		; в di зміщення пустого буферу

call	BinToAscDec		; заповнення буферу buffer 10-ковим 

; ASCII-представленням числа з ax 

mov 	cx,2				; в cx ширина поля виводу

call	StrWrite2			; вивід ASCII представлення номера дня

mov	cx,1				; в cx кількість виводів

mov 	dl, '\'				; в dl символ для виводу

call WriteSimv			; вивід символу із dl задану кількість разів

; ------Вивід місяця створення файлу

mov	ax,bx				; в ax слово місяця створення файлу

and 	ax,1e0h			; накладання маски

shr ax,5				; зсув результату (отримання місяця)

mov 	cx,1				; встановлення мінімальної довжини

; перетворюваного рядка

mov 	di,offset buffer		; в di зміщення пустого буферу

call	BinToAscDec		; заповнення буферу buffer 10-ковим

; ASCII-представленням числа з ax

mov 	cx,2				; в cx ширина поля виводу

call	StrWrite2			; вивід ASCII представлення номера дня

mov	cx,1				; в cx кількість виводів

mov 	dl, '\'				; в dl символ для виводу

call	WriteSimv			; вивід символу із dl задану кількість разів

; ------Вивід року створення файлу

mov	ax,bx				; в ax слово року створення файлу

and 	ax,0f800h			; накладання маски

shr ax,9				; зсув результату (отримання року)

add	ax,1980			; отримання року створення

mov 	cx,1				; встановлення мінімальної довжини

; перетворюваного рядка

mov 	di,offset buffer		; в di зміщення пустого буферу

call	BinToAscDec		; заповнення буферу buffer 10-ковим

; ASCII-представленням числа з ax

mov	cx,4				; в cx ширина поля виводу

call	StrWrite2			; вивід ASCII представлення номера дня

mov	cx,2				; в cx кількість виводів

mov 	dl, ' '				; в dl символ для виведення

call WriteSimv			; вивід проміжку у 2-а пробіли

; ------Вивід розміру файлу у 16-річному вигляді

mov ax,[offset dirData + FileName-2] ; в ax молодші два байти 

; подвійного слова розміру файла

mov 	cx,1				; в cx мінімальна довжина 

; перетворюваного рядка

mov 	di,offset buffer		; в di зміщення буферу для результату

call	BinToAscHex		; перетворення двох молодших байтів 

; розміну у ASCII-рядок

call	StrWrite			; вивід на екран

mov ax,[offset dirData + FileName-4] в ax містяться старші два байти 

; подвійного слова розміру файла

call	BinToAscHex		; перетворення у ASCII рядок 

; представлення у вигляді 16-річного 

; числа

call	StrWrite			; вивід на екран

call NewLine			; перехід на новий рядок

pop 	bx				; відновлення у bx зміщення процедури 

; Action

ret					; завершення процедури

ENDP Action

END	Start

Схема алгоритму основної програми приведена у додатках (Додаток А).

Отримання даних із командного рядку і встановлення критерію відбору файлів (маски) реалізується за допомогою функцій модуля PARAMS.obj. Спочатку заповнюється спеціальний буфер хвостом команди і його вміст розбивається на окремі елементи (GetParams). При наявності в хвості команди параметрів, перший параметр задає маску; якщо параметри відсутні, у якості маски встановлюється стандартна '*.*' (вивід всіх файлів із директорії, в якій знаходиться програма). Основну роботу виконує функція DirEngine, в яку, за допомогою регістру bx, передається зміщення функції Action (для подальшого виклику її за зміщенням).

Функція DirEngine, за допомогою 2Fh, 1Ah, 4Eh, 4Fh функцій переривання INT21h DOS, знаходить поточне положення буфера DTA, зберігає його адресу, встановлює новий DTA у власний спеціальний буфер і за допомогою нього шукає файли, що відповідають заданій масці. При знаходженні таких файлів, керування по зміщенню передається функції Action.

Основна задача Action – обробити інформацію в DTA і вивести необхідні дані на стандартний пристрій виводу.

DTA має наступну структуру [4, 6]:



Зміщення

Довжина



+0

15H

Зарезервована, використовується функцією 4Fh INT21h DOS

+15h

1

атрибут файла

+16h

2

час

+18h

2

дата

+1ah

4

розмір файла (формат DWORD)

+1eh

0dh

13-байтова ASCIIZ ім'я файлу



Спочатку виводиться ім'я файлу, яке має наступну структуру: "filename.ext". Вивід реалізується за допомогою спеціальних функцій StrWrite, StrWriter, Length і WriteSimv із модуля STRIO.obj.

Наступним кроком реалізується вивід атрибутів файлу шляхом виводу зірочок навпроти відповідного атрибуту. Байт атрибуту файлу має наступну структуру [6]:



№ біту

Що означає

0

тільки читання

1

скритий

2

системний

3

том

4

каталог

5

архів (або копія файлу не створювалась)



Тобто, якщо біт атрибуту файлу буде мати наступне значення 21h, це означатиме, що файл є архівним, він лише для читання.

Передостаннім кроком виводяться час і дата створення файлу у форматі: година:хвилина:секунда день\місяць\рік. Дані, що описують час і дату створення/змінення файлу, потребують накладання масок і здвигів для 

№ біта



Операція



№ біта



Операція

Час створення



Дата створення

15

година

(0-23)

(t & 0f800h)>>11



15

рік

(0-119)+1980

d & 001fh

14







14





13







13





12







12





11







11





10

хвилина

(0-59)

(t & 07e0h)>>5



10





9







9





8







8

місяць

(1-12)

(d & 01e0h)>>5

7







7





6







6





5







5





4

секунда / 2

(0-30)

t & 001fh



4

день

(0-31)

(d & f800h)>>9

3







3





2







2





1







1





0







0





отримання необхідної інформації і представлені в наступному виді [6]:





Перевід двійкових чисел у ASCII-десяткове представлення для виводу на екран реалізується за допомогою функції BinToAscDec модуля BINASC.obj.

Останній крок виводу розміру файлу виконується в два етапи, оскільки розмір файлу міститься у подвійному слові і модуль BSNASC.obj не надає інструменту для переведення двійкових даних у ASCII-рядок.

Спочатку за допомогою функції BinToAscHex модуля BINASC.obj переводиться молодші, а потім старші два байти, переведені у ASCII-шістнадцятирічне представлення.

По завершенні своєї роботи Action знов передає керування викликаючій функції DirEngin, яка повторює пошук файлів і викликає для роботи Action, доки не завершить обробку всіх файлів, що відповідають масці. Після цього DirEngine повертає старий DTA і передає курування основній програмі, яка завершує роботу і повертає код виходу.

Результати роботи програми:

D:\Program\ASM\misk>dr

The DIRWUER wersion 1.0

Romanov Alexander Urievich. KIT-13A NTU"KhPI"

Copyright (C) 2005 by Romanov Alexander

Filename OnR Skr Sys Tom Kat Arh Time Data Size

. * 11:59:18 11\5 \2005 00

.. * 11:59:18 11\5 \2005 00

BLW32.DLL * 12:12:0 14\5 \1996 52020

DEBUG.EXE * * 22:22:0 5 \5 \1999 0518A

USA.BLL * 12:12:0 14\5 \1996 18DCD

MYREP.CBA * 18:48:42 25\1 \2002 01F

DIRECT * 12:27:54 11\5 \2005 00

CATALOG3.CAB * * 22:22:0 5 \5 \1999 425C3

SUHELPER.BIN * * 22:22:0 5 \5 \1999 05C0

SAVE32.COM * * 22:22:0 5 \5 \1999 0398

ASD.LOG * * 13:8 :20 11\8 \2004 0162

IO.SYS * * * 22:22:0 5 \5 \1999 364B6

MSDOS.SYS * * * * 18:30:28 16\7 \2004 0697

08-APRIL.MP3 * 11:42:44 7 \12\2003 B11100

DR.EXE * 12:14:28 11\5 \2005 05A1

DR.BAT * 12:54:34 4 \5 \2005 09B



D:\Program\ASM\misk>dr.exe *.exe

The DIRWUER wersion 1.0

Romanov Alexander Urievich. KIT-13A NTU"KhPI"

Copyright (C) 2005 by Romanov Alexander

Filename OnR Skr Sys Tom Kat Arh Time Data Size

DEBUG.EXE * * 22:22:0 5 \5 \1999 0518A

DR.EXE * 12:14:28 11\5 \2005 05A1



D:\Program\ASM\misk>dr s*.???

The DIRWUER wersion 1.0

Romanov Alexander Urievich. KIT-13A NTU"KhPI"

Copyright (C) 2005 by Romanov Alexander

Filename OnR Skr Sys Tom Kat Arh Time Data Size

SUHELPER.BIN * * 22:22:0 5 \5 \1999 05C0

SAVE32.COM * * 22:22:0 5 \5 \1999 0398



D:\Program\ASM\misk>dr d:\program\asm\misk\direct\*.*

The DIRWUER wersion 1.0

Romanov Alexander Urievich. KIT-13A NTU"KhPI"

Copyright (C) 2005 by Romanov Alexander

Filename OnR Skr Sys Tom Kat Arh Time Data Size

. * 12:27:54 11\5 \2005 00

.. * 12:27:54 11\5 \2005 00

BINASC.ASM * 11:3 :40 8 \5 \2005 0300

PARAMS.ASM * 13:31:20 6 \5 \2005 04E4

TD.EXE * 14:39:40 24\12\2002 784F0

Як видно із приведених результатів програма DR.EXE за умовчанням виводить весь вміст директорії, в якій вона розташована (приклад 1). Інші приклади демонструють різноманітні засоби виклику програми із заданням різноманітних масок, використовуючи символи "*" і "?". Останній приклад демонструє, що виклик DR.exe можна реалізувати і з маскою, що задає повний шлях до директорії, інформацію про яку необхідно вивести.

Таким чином, розроблена програма DR.exe повністю відповідає поставленому завданню про створення програми мовою асемблера, яка виводить вміст директорії, атрибути, розмір і час/дату створення файлів і папок, які в ній містяться.

4. Розробка задачі на мові високого рівня із використанням асемблерних фрагментів коду



Як показала третя глава, розробка програми чисто на асемблері є складним процесом і потребує розробки супутніх (допоміжних) модулів для реалізації моментів, що не пов’язані із основним алгоритмом задачі (таких, як вивід на екран, перетворення двійкових даних у ASCII-рядки, тощо). Мови високого рівня дають можливість реалізувати все автоматично [3]. Такий стан речей спонукає розробити програму, в якій основний алгоритм пошуку DTA і необхідних файлів, що відповідають масці, реалізовані асемблерними вставками, а задачі, пов’язані із розробкою інтерфейсу і виводу даних на екран, реалізовані, наприклад, мовою Pascal.



4.1. Розробка програми на Pascal



Мова Pascal дає можливість створити вказівник типу "запис" (поля "запису" описують атрибут, час, дату, розмір і ім'я файлу) прямо на стандартний буфер DTA, не створюючи власного і не виконуючи зайвих дій по встановленню нової адреси буфера DTA і відновлення старої після завершення програми. Вивід інформації про файл довіряється процедурі, яка обробляє поля вказівника на DTA і виводить дані стандартними функціями Pascal.

На початку програми є сенс запитувати користувача про бажання задати власну маску для файлів, або лишити стандартну ("*.*").

Згідно із зазначеними змінами було розроблено програму DIRWUER.pas, приведену нижче:

{DIRWUER.pas}

Uses crt;

{------FindFirst – шукає перше входження файлу, що відповідає заданій масці}

Function FindFirst (Path : PChar) : Boolean; assembler;

asm

mov ah, 4Eh			{в ah номер функції першого пошуку}

mov cx, 3fh				{в cx маска для всіх атрибутів файлу}

mov dx, word ptr Path		{dx вказує на маску файлу}

int 21h				{виклик функції першого пошуку файлу}

mov al, 0				{в al поміщується 0 – код помилки (false)}

jc @Failed				{якщо помилка, повернути код помилки,}

inc al					{інакше повернути 1 – true }

@Failed:

end;

{------FindNext шукає наступне входження файлів, маска і атрибути яких були задані при попередньому пошуку.}

Function FindNext : Boolean; assembler;

asm

mov ah, 4Fh				{в ah номер функції наступного пошуку }

					{файлу}

int 21h				{пошук наступного файлу за параметром,}

					{заданим функцією 4Eh}

mov al, 0				{в al поміщується 0 – код помилки (false)}

jc @Failed				{повернути код помилки, якщо помилка}

inc al					{інакше повернути 1 – true }

@Failed:

end;

Type

DTA = record			{запис описує структуру DTA-буфера}

	Reserved : Array[0..$14] of Byte;	{резервна область пам'яті}

	Attrib	 : Byte;				{поле атрибуту}

	Time	 : Word;				{поле часу створення}

	Date	 : Word;				{поле дати}

	Size	 : Longint;				{розмір}

	Name	 : Array[0..$C] of Char;		{ім'я файлу із розширенням}

	end;

PDTA = ^DTA;			{вказівник на буфер DTA}

{------GetDTAAddress повертає адресу буфера DTA}

Function GetDTAAddress : PDTA; assembler;

asm

mov ah, 2Fh				{в ah номер функції пошуку DTA}

int 21h				{отримання в as:bx адреси DTA }

push es				{в dx через стек передаються дані із es}

pop dx				

mov ax, bx				{передача в ax даних із bx}

{в dx:ax повертається результат}

end;



Var

DTAAddress 	: PDTA;

	s		: string;

Path 			: PChar;

	i		: byte;

Label		1;

{------Процедура виводу даних о файлі/директорії згідно із інформацією в DTA}

Procedure ShowEntry;

Begin

Write(DTAAddress^.Name:13,' ');	{вивід імені файлу із розширенням}

for i: = 0 to 5 do

if (DTAAddress^.Attrib and (1 shl i)) <>0 

then write(' * ')	{вивід зірочок навпроти файлів }

else write(' ');	{із відповідними атрибутами}

{------Вивід часу створення файлу}

Write(' ',((DTAAddress^.Time and $0f800)shr 11):2);

Write(':',((DTAAddress^.Time and $07e0)shr 5):2,':'); 

Write(((DTAAddress^.Time and $1f)shl 1):2); 

{------Вивід дати створення файлу}

Write(' ',(DTAAddress^.Date and $1f):2,'/');

Write(((DTAAddress^.Date and $01e0)shr 5):2,'/');

Write((((DTAAddress^.Date and $0f800)shr 9)+$07bc):4); 



Writeln(' ',DTAAddress^.Size:7); 	{вивід розміру файлу}

end;



BEGIN

ClrScr;						{очистка екрану}

{------Вивід інформації о програмі}

Writeln('The DIRWUER wersion 1.0'); 

Writeln('Romanov Alexander Urievich. KIT-13A KHPI');

Writeln('Copyright (C) 2005 by Romanov Alexander'); 

DTAAddress := GetDTAAddress;	{встановлення вказівника}

{на адресу DTA}

1:

Write('Input the mask (default: *.*; exit: q): '); {запит на введення маски}

Readln(s);					{отримання відповіді}

if s=''	then Path:='*.*'			{якщо відповіді не має,}

{встановлення стандартної маски}

else if s='q'	then halt		{якщо відповідь 'q', завершення}

{роботи програми}

else Path:=Addr(s[1]); {встановлення заданої маски}

if FindFirst (Path)	then			{знаходження першого файлу}

begin

	Write('Filename		OnR Skr Sys Tom ');

Writeln('Kat Arh Time DataSize'); {вивід заголовка таблиці}

	ShowEntry;				{вивід на екран даних про файл}

	while FindNext do ShowEntry;	{доки знайдено наступний файл,}

{виводити дані про нього на екран}

end

			else writeln('Failed');{інакше, при незнаходженні}

{жодного файлу, виводиться}

{повідомлення про їх відсутність}

goto 1;					 {перехід на початок}

END.

Схема алгоритму програми приведена в додатках (Додаток Б).

Програма реалізує запит на введення маски файлу і, згідно із реакцією користувача, встановлює стандартну маску, або введену користувачем. При відповіді q – програма завершується.

Після задання маски встановлюється вказівник на DTA-буфер, після чого програма реалізує пошук файлів, що відповідають масці і вивід даних на екран. Потім програма повертається до початкового кроку запиту нової маски файлів для наступних дій.

Результати роботи програми проілюструємо наступним чином: 

The DIRWUER wersion 1.0

Romanov Alexander Urievich. KIT-13A KHPI

Copyright (C) 2005 by Romanov Alexander

Input the mask (default: *.*; exit: q):

Filename OnR Skr Sys Tom Kat Arh Time Data Razmer

 . * 11:59:18 11/5 /2005 00

 .. * 11:59:18 11/5 /2005 00

 BLW32.DLL * 12:12:0 14/5 /1996 52020

 DEBUG.EXE * * 22:22:0 5 /5 /1999 0518A

 USA.BLL * 12:12:0 14/5 /1996 18DCD

 MYREP.CBA * 18:48:42 25/1 /2002 01F

 DIRECT * 12:27:54 11/5 /2005 00

 CATALOG3.CAB * * 22:22:0 5 /5 /1999 425C3

 SUHELPER.BIN * * 22:22:0 5 /5 /1999 05C0

 SAVE32.COM * * 22:22:0 5 /5 /1999 0398

 ASD.LOG * * 13:8 :20 11/8 /2004 0162

 IO.SYS * * * 22:22:0 5 /5 /1999 364B6

 MSDOS.SYS * * * * 18:30:28 16/7 /2004 0697

 08-APRIL.MP3 * 11:42:44 7 /12/2003 B11100

 DR.EXE * 12:14:28 11/5 /2005 05A1

 DR.BAT * 12:54:34 4 /5 /2005 09B

 DIRWUER.EXE * 13:48:38 13/ 5/2005 6256 



Input the mask (default: *.*; exit: q): *.b??

Filename OnR Skr Sys Tom Kat Arh Time Data Razmer

 USA.BLL * 12:12: 0 14/ 5/1996 101837

 SUHELPER.BIN * * 22:22: 0 5/ 5/1999 1472

 DR.BAT * 12:54:34 4/ 5/2005 155



Input the mask (default: *.*; exit: q): direct/*.*

Filename OnR Skr Sys Tom Kat Arh Time Data Razmer

 . * 12:27:54 11/ 5/2005 0

 .. * 12:27:54 11/ 5/2005 0

 BINASC.ASM * 11: 3:40 8/ 5/2005 768

 PARAMS.ASM * 13:31:20 6/ 5/2005 1252

 TD.EXE * 14:39:40 24/12/2002 492784



Input the mask (default: *.*; exit: q): q

Аналізуючи отримані результати, зазначимо, що за умовчанням (перший приклад) програма DIRWUER.exe виводить увесь вміст директорії, в якій вона розташована. Інші приклади демонструють різноманітні засоби задання масок файлів із використанням символів "*" і "?", а також задання шляху до директорії, вміст якої необхідно вивести. Відповідь "q" завершує програму.

Таким чином DIRWUER.exe є повноцінною програмою, яка реалізує вивід інформації о файлах, що відповідають заданій масці. Відмінність програми DIRWUER.exe від DR.exe полягає в покращеному інтерфейсі і більш ефективній роботі із буфером DTA, що стало можливим при використанні мови високого рівня Pascal, як оболонки при написанні програми.

Висновки



За результатами дослідження виводу вмісту каталогу у середовищі MS DOS було проаналізовано деякі аспекти його роботи із файлами і консоллю і створено дві повноцінні програми мовами асемблер і Pascal, які дозволяють реалізувати перегляд дерева каталогів і отримувати всю інформацію про файли, що в них містяться.

Слід зазначити, що у тому разі, коли програма, написана мовою асемблер, має розмір лише 1441 байт, то програма, створена за допомогою мови Pascal, займає вже 6256 байт, тобто більше, ніж у чотири рази. Однак витрати на написання цих програм співвідносяться приблизно у тій же пропорції, але в інший бік, оскільки ті задачі, які мовою Pascal були вирішені вбудованими методами, мовою асемблер треба було виконувати власноруч.

Таким чином, мета даного курсового проекту вважається виконаною: було досліджено особливості програмування мовами різного рівня, а також на конкретному прикладі виводу вмісту каталогу у середовищі MS DOS продемонстровані переваги та недоліки, котрі при цьому виникають. 



Список використаних джерел



Методические указания к лабораторным работам № 10–№ 18 по курсу "Архитектура вычислительных систем" / Составит. А.И. Поворознюк, И.С. Зыков. – Харьков: ХПИ. – 88 с.

Поворознюк А.И. Архитектура компьютеров. Архитектура микропроцессорного ядра и системных устройств: Учеб. пособие. Ч.2. – Харьков: Торнадо, 2004. – 355 с.

Пустоваров В.И. Язык Ассемблера в программировании информационных и управляющих систем. – М.: ЭНТРОП, 1997. – 304 с.

Сван Т. Освоение Turbo Assembler. – К.: Диалектика, 1996. – 544 с.

Белецкий Я. Турбо Ассемблер: Версия 2.0: Учеб. пособие для студентов вузов / Пер. с польск. В.В. Яценко. М.: Машиностроение, 1994. – 160 с.

Rollins D. TECH Help. Электронный справочник. Версия 1.2. / Адапт. С.М. Абель. – Flambeaux Software. –© 1985–1987. 

Norton P. Peter Norton's DOS Guide. – New Delhi: Prentice Hall of India Private Limited, 1996. – 744 p.

Додатки



Додаток А



Схема алгоритму DR.asm



\ Початок



Встановлення сегменту даних

GetParams

Отримання хвоста команди командного рядку



Інформація о програмі

ParamCount

Отримання в cx кількість параметрів

cx ≠ 0

OneParam

Вибір маскою першого параметру командного рядку

Встановлення стандартної маски 

“ *  “

А

/ Кінець

В bx зміщення функції виводу інформації о файлі

DirEngine

Виклик функції пошуку файлів, що задовольняють масці

Завершення роботи і повернення коду возврату

А

−

+



Додаток Б



Схема алгоритму DIRWUER.pas



\ Початок



Path = s

GetDTAAddress

Отримання адреси DTA

Вивід інформації о програмі

s = ""



Path = '*.*'

А

Отримання маски файлу у змінну s

Запит на введення маски файлу

s = "q"

/ Кінець

B

−

+

−

+


Схема алгоритму DIRWUER.pas (продовження)





B



Повідомлення про помилку

А



Вивід заголовка таблиці

ShowEntry

Вивід інформації о файлі

ShowEntry



Вивід інформації о файлі

Перше знаходження файлу, що відповідає масці

Знайдено наступний файл

−

+

−

+





2



6