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), вы можете повторно их исполь- зовать. Если сформировать библиотеку полезных подпрограмм, то это может в последующем существенно облегчить и упростить разработку программ.