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 підраховує кількість ненульових символів в рядку
; -------------------------------------------------------------------------------------------