Пример программы на языке Ассемблера


Давайте теперь попробуем реализовать все то, что мы узнали в

последних двух главах, в виде полезного примера программы на Ас-

семблере. Эта программа 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), вы можете повторно их исполь-

зовать. Если сформировать библиотеку полезных подпрограмм, то это

может в последующем существенно облегчить и упростить разработку

программ.

Загрузка...