CodeNet / Языки программирования / Ассемблер / СПРАВОЧНИК по системе программирования ТУРБО АССЕМБЛЕР 2.0
СПРАВОЧНИК по системе программирования ТУРБО АССЕМБЛЕР 2.0
Пример программы на языке Ассемблера
-----------------------------------------------------------------
Давайте теперь попробуем реализовать все то, что мы узнали в
последних двух главах, в виде полезного примера программы на Ас-
семблере. Эта программа WCOUNT.ASM подсчитывает число слов в фай-
ле и выводит его на экран.
;
; Программа для подсчета числа слов в файле. Слова разделены
; пробелами, символами табуляции, возврата каретки или перевода
; строки.
;
; Вызов: wc < имя_файла.расш
;
DOSSEG ; выбрать стандартный
; порядок сегментов
.MODEL SMALL ; код и данные
; помещаются в 64К
.STACK 200h ; стек размером 512
; байт
.DATA
Count DW 0 ; используется для
; подсчета слов
InWhitespace DB ? ; устанавливается в
; значение 1, когда
; последним прочитанным
; символов является
; разделитель
TempChar DB ? ; временная память,
; используемая
; в GetNextCharacter
Result DB 'Число слов: ', 5 DUP (?)
; строка, используемая
; для вывода результата
CountInsertEnd LABEL BYTE ; используется для
; определения конца
; области, в которой
; хранится строка со
; значением счетчика
DB 0dh,0ah,'$' ; функция DOS 9
; работает со строками,
; которые завершаются
; символом $
.CODE
ProgramStart:
mov ax,@Data
mov ds,ax ; DS указывает на
; сегмент данных
mov [InWhitespace],1 ; предположим, это
; разделитель, так как
; первый отличный от
; разделителя символ,
; который мы найдем,
; будет отмечать начало
; слова
CountLoop:
call GetNextCharacter ; получить следующий
; символ для проверки,
jz CountDone ; если он имеется
call IsCharacterWhitespace ; это разделитель?
jz IsWhitespace ; да
cmp [InWhitespace],0 ; символ не является
; разделителем - теперь
; мы в разделителе?
jz CountLoop ; мы не в разделителе
; и символ не является
; разделителем, поэтому
; с этим символом работа
; окончена
inc [Count] ; мы в разделителе и
; символ не является
; разделителем, значит
; мы нашли начало нового
; слова
mov [InWhitespace],0 ; отметить, что мы боль-
; ше не в разделителе
jmp CountLoop ; обработать следующий
; символ
IsWhitespace:
mov [InWhitespace],1 ; отметить, что мы в
; разделителе
jmp CountLoop ; обработать следующий
; символ
;
; Подсчет завершен - вывести результаты
;
CountDone:
mov ax,[Count] ; число, которое нужно
; преобразовать в строку
mov bx,OFFSET CountInsertEnd-1 ; ссылка на
; конец строки, в
; которую нужно
; поместить число
mov cx,5 ; число цифр, которые
; нужно преобразовать
call ConvertNumberToString ; преобразовать
; число в строку
mov bx,OFFSET Result ; ссылка на строку
; результата
call PrintString ; вывести результат
mov ah,4ch ; функция DOS
; завершения программы
int 21h ; завершить программу
;
; Подпрограмма получения следующего символа из стандартного
; ввода
;
; Входные данные: нет
;
; Выходные данные:
; AL = символ, если он был доступен
; флаг Z = 0 (NZ), если символ доступен,
; = 1 (Z) при достижении конца строки
;
; Нарушаемые регистры: AH, BX, CX, DX
;
GetNextCharacter PROC
mov ah,3fh ; функция DOS
; чтения из файла
mov bx,0 ; стандартный
; описатель ввода
mov cx,1 ; считать один символ
mov dx,OFFSET TempChar ; поместить символ
; в TempChar
int 21h ; получить следующий
; символ
jc NoCharacterRead ; если DOS сообщает
; об ошибке,
; интерпретировать ее,
; как конец файла
cmp [TempChar],1ah ; это Control-Z?
; (метка конца файла)
jne NotControlZ ; нет
NoCharacterRead:
sub ax,ax ; установить флаг Z,
and ax,ax ; что отражает, был
; ли считан символ (NZ)
; или мы достигли
; конца файла (Z).
; Обратите внимание,
; что функция DOS 3fh
; устанавливает регистр
; AX в значение числа
; считанных символов
mov al,[TempChar] ; возвратить считанный
; символ
ret ; выполнено
GetNextCharacter ENDP
;
; Подпрограмма, сообщающая, является ли прочитанный символ
; разделителем
;
; Входные данные:
; AL = проверяемому символу
;
; Выходные данные:
; флаг Z = 0 (NZ), если символ не является разделителем,
; = 1 (Z) если символ - разделитель
;
; Нарушаемые регистры: нет
;
IsCharacterWhitespace PROC
cmp al,09h ; это символ табуляции?
jz EndIsCharacterWhitespace ; если да, то
; это разделитель
cmp al,' ' ; это пробел?
jz EndIsCharacterWhitespace ; если да, то
; это разделитель
cmp al,0dh ; это возврат каретки?
jz EndIsCharacterWhitespace ; если да, то
; это разделитель
cmp al,0ah ; это перевод строки?
cmp al,' ' ; это пробел?
; если да, то это
; разделитель,
; возвратить Z, если
; нет, то это не
; разделитель, возвратить
; NZ (устанавливаться
; cmp)
EndIsCharacterWhiteSpace:
ret
IsCharacterWhiteSpace ENDP
;
; Подпрограмма, преобразующая двоичное число в текстовую
; строку
;
; Входные данные:
; AX = число, которое нужно преобразовать
; DS:BX = указатель на конец строки, в которой
; сохраняется текст
;
; Выходные данные: нет
;
; Нарушаемые регистры: AX, BX, CX, DX, SI
;
ConvertNumberToString PROC
mov si,10 ; используется в цикле
; ConvertLoop
sub dx,dx ; преобразовать AX в
; двойное слово в AD:DX
div si ; разделить число на 10
; остаток - в DX, это
; десятичное число из
; одной цифры; число/10
; находится в AX
add dl,'0' ; преобразовать остаток
; в текстовую строку
mov [bx],dl ; поместить эту цифру в
; строку
dec bx ; ссылка на следующую
; самую значащую цифру
loop ConvertLoop ; обработать следующую
; цифру, если она есть
ret
ConvertNumberToString ENDP
;
; Подпрограмма, выводящая строку на экран дисплея
;
; Входные данные:
; DS:BX = указатель на выводимую строку
;
; Выходные данные: нет
;
; Нарушаемые регистры: нет
;
PrintString PROC
push ax ; сохранение регистров
push dx ; в подпрограмме
mov ah,9 ; функция DOS вывода
; строки
mov dx,bx ; установить DS:DX на
; выводимую строку
int 21h ; вызвать DOS для
; вывода строки
pop dx ; восстановить измененные
pop ax ; регистры
ret ; возврат управления
PrintString ENDP
END ProgramStart
Выполняемый файл WCOUNT.EXE можно запускать в ответ на подс-
казку DOS, переназначив ввод из файла, в котором вы хотите подс-
читать слова. Например, для подсчета числа слов в файле
WCOUNT.ASM нужно в ответ на подсказку DOS ввести:
wcount <wcount.asm
Через несколько секунд на экран будет выведен результат:
Число слов: nnn
(где вместо nnn будет стоять конкретное число).
Относительно файла WCOUNT.ASM можно сделать несколько инте-
ресных замечаний. Во-первых, для выполнения различных функций при
чтении символа, проверки символа на разделитель, преобразования
числа слов в строку и вывода строки в файле WCOUNT.ASM использу-
ются подпрограммы. Это позволяет сделать основную программу файла
WCOUNT.ASM небольшой по размеру и простой для понимания.
Другое преимущество использования подпрограмм заключается в
простоте, с какой вы можете изменять работу подпрограмм. Если,
например, вам потребуется изменить определение разделителя и
включить в число разделителей знак равенства, вы можете изменить
подпрограмму IsWhitespace, оставив основную программу без измене-
ний.
Заметим, что подпрограммы GetNextCharacter и IsWhitespace
возвращают информацию о состоянии во влаге нуля (подпрограмма
GetNextCharacter возвращает также информацию в регистре AL). Флаг
нуля прекрасно подходит для возврата состояния типа "да/нет", а
регистр AL (или AX) хорошо использовать для возврата значений.
Наконец, обратим внимание на объем кода на Ассемблере, необ-
ходимый для выполнения операций по выводу текста. Чтобы вывести
на экран целое значение (число подсчитанных слов), нам пришлось
сначала преобразовать значение счетчика в текстовую строку (пов-
торно выполняя деление на 10 и добавляя к остатку символ "0").
Только после этого вызвали DOS для вывода текстовой строки. Это
сильно отличается от простого оператора на языке Си:
print("Число слов: dn",count);
С другой стороны, после того, как вы напишете подпрограммы
(такие, как ConvertNumberToString), вы можете повторно их исполь-
зовать. Если сформировать библиотеку полезных подпрограмм, то это
может в последующем существенно облегчить и упростить разработку
программ.
