Дана функція записує CXбайт даних в файл, або пристрій, заданий описувачем в BX. В AHпоміщується номер функції, BXмістить описувач[6]:
0 Стандартний пристрій вводу (звичайна клавіатура)
1 Стандартний пристрій виводу (звичайний екран)
2 Стандартний пристрій помилок (CON-екран)
3 Стандартний пристрій AUX(COM1)
4 Стандартний принтер (LPT1)
Регістрова пара DS:DXадресується на буфер. В CXміститься кількість записуваних байт.
Функція повертає в AXкод помилки при умові, що CFвстановився в 1, або кількість реально зчитаних байтів в AL.
Для нас дана функція цікава як інструмент для виводу даних на екран, тому в BXмає бути занесено 1.
Приклад:
movah, 40h ; код функції
movbx, 1 ; вивід на екран
movdx, offsetsstring ; в dxзміщення рядка виводу
movcx, FFh ; в cxкількість символів, що виводяться
int 21h
Даний фрагмент коду виводить на екран рядок символів, що містяться в змінній sstring.
4Chфункція INT21h[6]
Вхід: AH 4Ch
AL код виходу
Вихід: не має
Функція завершення програми (EXIT). Повертає управління від породженого процесу його батьківському процесу. Встановлює код виходу (його можна опитати функцією WAIT(4Dh)).
В AХміститься номер функції, в AL– код виходу:
0 нормальне завершення
1 завершення через Ctrl-Break(INT23h)
2 завершення по критичній помилці пристрою (INT24h)
3 завершення через функцію KEEP(31h)
Приклад:
movax, 04ch ; в al– код виходу
int 21h ; в ah– номер функції
Даний фрагмент коду задає нормальне завершення роботи програми (повертається код виходу – 0).
3. Розробка задачі на мові асемблер
3.1. Допоміжні процедури
Очевидно, що основна програма потребує допоміжні процедури для отримання необхідних параметрів, які задає користувач в командному рядку при визові програми. Серед них: процедури вводу/виводу даних на консоль, обробки ASCIIрядків, а також перетворення числових даних у ASCIIформат для подальшого їх виведення на екран. Для цього були розроблені спеціальні модулі PARAMS.asm, STRIO.asmта BINASC.asm, які містять необхідні процедури. Розглянемо їх окремо.
3.1.1. Модуль PARAMS.asm
Традиційно, програми MSDOSдозволяють користувачу вводити в командному рядку одне чи більше імен файлів і інші дані. Для нас це цікаве можливістю одразу при визові основної програми 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
MODELsmall
TailLenEQU0080h ; зміщення байта із довжиною рядка
; параметрів
CommandTailEQU0081h ; зміщення першого символу рядка
; параметрів
DATASEG
numParamsDW? ; кількість параметрів
paramsDB128 DUP(?) ; буфер на 128 байт для хвоста команди
CODESEG
PUBLIC ParamCount, GetParams, GetOneParam
; -------------------------------------------------------------------------------------------
; Separators внутрішня процедура для перевірки на пробіли, табуляцію,
; повернення каретки
; ------------------------------------------------------------------------------------------
; Вхід ds:si адреса символу, що перевіряється
; Вихід ZF=1 символ є пробілом, табуляцією чи поверненням каретки
ZF=1 символ не є роздільником
; Регістри не змінюються
; -------------------------------------------------------------------------------------------
PROC Separators
push ax ; збереження у стеку ax
moval, [si] ; в alпоміщується символ із ds:si
cmpal, 020h ; порівняння alіз пробілом
je @@10 ; якщо так, то перехід
cmpal, 009h ; порівняння alіз табуляцією
je @@10 ; якщо так, то перехід
cmpal, 00Dh ; порівняння alіз символом повернення
; каретки
@@10:
popax ; відновлення ax
ret ; повернення до викликаючої програми
ENDPSeparators
; -------------------------------------------------------------------------------------------
; ParamCount повертає кількість параметрів у хвості команди
; -------------------------------------------------------------------------------------------
; Вхід не має
; Вихід CX кількість параметрів командного рядка
; Регістри CX
; -------------------------------------------------------------------------------------------
PROCParamCount
movcx, [numParams] ; отримати значення змінної numParams
ret ; повернення до викликаючої програми
ENDPParamCount
; -------------------------------------------------------------------------------------------
; GetParams занесення параметрів командного рядка DOSу буфер
; -------------------------------------------------------------------------------------------
; Вхід ds префікс сегмента програми (PSP) (адресує PSP, якщо його
; не змінювали)
; es сегмент даних програми
; Вихід [params] початок буфера заповненого даними
; [numParams] кількість параметрів
; ds сегмент даних програми
; Регістри al, bx, dx, si, di, ds
; -------------------------------------------------------------------------------------------
PROCGetParams
;------Ініціалізація cxі індексних регістрів siі di
pushax ; збереження регістрів
pushbx
push dx
push si
push di
xor ch, ch ; обнуління верхньої половини cx
movcl, [ds:TailLen] ; в cxдовжина параметрів
inccx ; включити символ повернення каретки
movsi, CommandTail ; адреса параметрів поміщується в si
movdi, offsetparams ; адреса призначення поміщується в di
; ------Пропуск початкових пробілів і табуляції
@@10:
callSeparators ; пропуск пробілів і табуляції
jne @@20 ; перехід, якщо пробілів і табуляції не має
incsi ; пропуск символу
loop @@10 ; цикл, доки не скінчиться обробка, абоcx=0
; ------Копіювання параметрів рядка в буфер params
@@20:
pushcx ; збереження cxу стеку
jcxz @@30 ; пропуск копіювання, якщо cx=0
cld ; збільшення на 1 siі di
repmovsb ; копіювання cxбайтів із ds:siв es:di
; ------Перетворення пробілів в 0 і встановлення numParams
@@30:
push es
pop ds ; ds=es
popcx ; відновлення cx(довжину)
xorbx, bx ; обнуління bx, лічильник параметрів
jcxz @@60 ; пропуск циклу якщоcx=0 (довжина)
movsi, offsetparams ; поміщення адреси параметрів в si
@@40:
callSeparators ; перевірка на пробіли, табуляцію,
; повернення каретки
jne @@50 ; перехід, якщо не знайдено роздільник
mov [byteptrsi], 0 ; заміна роздільника на 0
incbx ; збільшення лічильника кількості
; параметрів
@@50:
incsi ; переміщення указника на наступний
; символ
loop @@40 ; виконувати в циклі, доки cx≠0
@@60:
mov [numParams], bx ; збереження в numParamsкількість
; параметрів
popax ; відновлення регістрів
popbx
popdx
pop si
pop di
ret ; повернення до батьківської програми
ENDPGetParams
; -------------------------------------------------------------------------------------------
; GetOneParam отримати адресу параметра за номером
; -------------------------------------------------------------------------------------------
; Вхід cx номер параметра (має бути менше значення в numParams)
; Вихід di зміщення ASCIIрядка із потрібним параметром
; Регістри di
; -------------------------------------------------------------------------------------------
PROCGetOneParam
pushax ; збереження регістрів axі cx
pushcx
xoral, al ; обнуління al(ініціалізація шуканого
; значення 0)
movdi, offsetparams ; адреса параметрів рядка
jcxz@@99 ; якщо номер параметра(cx) дорівнює 0,
; то вихід
cmpcx, [numParams] ; порівняння cxіз кількістю параметрів
jae @@99 ; вихід, якщо передано неіснуючого
; параметру
cld ; автоматичне збільшення di
@@10:
scasb ; пошук нульового обмежувача
jnz @@10 ; повтор, доки не знайдено 0
loop @@10 ; повтор, доки в cxне буде 0
@@99:
popcx ; відновлення регістрів cx, ax
popax
ret ; повернення до викликаючої програми
ENDPGetOneParam
END
Таким чином, програмний модуль PARAMS.asmє зручним інструментом для реалізації роботи із командним рядком і буде використаний в основній програмі.
3.1.2. Модуль STRIO.asm
Оскільки важливою частиною основної програми, згідно із завданням, буде вивід текстових рядків на екран, то є необхідність у створенні спеціального програмного модуля, який би містив процедури для обробки і виводу ASCIIрядків на екран. Пряме використання функцій DOSв основній програмі є незручним, оскільки є потреба у спрощенні коду для його сприйняття.
З цих міркувань було розроблено програмний модуль STRIO.asm, в якому міститься п’ять спеціальних функцій: StrLength (визначає кількість символів, записаних в ASCIIрядку), дві функції виводу ASCII-рядків на екран – StrWriteі StrWrite2, а також функцію NewLine(перехід на новий рядок) та WriteSimv(виводить на екран заданий символ необхідну кількість разів).
Слід зазначити, що даний програмний модуль не містить функцій читання із консолі в рядок, однак основна програма отримує дані із PSPDOS-а і опрацьовує вже створені дані, а тому не потребує якихось додаткових вказівок через консоль від користувача, всі необхідні специфічні дані (наприклад, маска файлів) користувач може задати в командному рядку при визові основної програми.
Код програмного модуля STRIO.ASMприведений нижче:
IDEAL
MODEL small
ASCnull EQU 0 ; ASCIIнуль
ASCcr EQU 13 ; ASCIIсимвол повернення каретки
ASClfEQU 10 ; ASCIIсимвол вертикальної табуляції
; (прогону рядка)
CODESEG
PUBLIC StrLength, StrWrite, StrWrite2, NewLine, WriteSimv
; ------------------------------------------------------------------------------------------
; StrLength підраховує кількість ненульових символів в рядку
; -------------------------------------------------------------------------------------------
; Вхід di адреса ASCIIрядка
; Вихід cx кількість ненульових символів в рядку
; Регістри cx
; -------------------------------------------------------------------------------------------
PROCStrLength
pushax ; зберегти у стеку змінювані