- •Язык ассемблера и программирования Глава 1. Директивы языка ассемблера Директива segment
- •Директива group
- •Директива assume
- •Assume сегментный_регистр : имя [, ...]
- •Директива Proc
- •Директива macro
- •Оформление программ в виде отдельных модулей
- •Передача параметров в подпрограмму
- •Описание
- •Директивы упрощенного описания сегментов Директива model
- •Вызов процедур с использованием кадра стека
- •Глава 2. Вывод информации на терминал в текстовом режиме Вывод символьной строки. Модуль puts.
Вызов процедур с использованием кадра стека
Для этого используется расширенный синтаксис инструкции CALL. Синтаксис вызова процедуры приобретает следующий вид:
Call выражение [язык] [, аргументы]
где выражение определяет адрес вызываемой процедуры; язык указывает соглашения, по которым будет осуществляться вызов. Если язык не указан, то используется соответствующий параметр из директивы .MODEL. Пример:
call test C, ax, es offset bufer, word ptr blen
то будет сгенерирован следующий код:
push word ptr blen
push es offset bufer
push ax
call test
sub sp,8
На рис 1.16 представлена вызывающая программа, использующая вызов процедуры с использованием кадра стека.
LBUF=82
EXTRN _gets:near
.MODEL small,C
.DATA
string db LBUF dup(?)
.STACK 100h
.CODE
beg proc far
.STARTUP
call _gets,offset string
.EXIT
beg ENDP
END beg
Рис. 1.16.
Используя макрос, можно вызов функции gets привести к виду, ипользуемому в языке Си для вызова аналогичной функции:
gets MACRO outstr
call _gets, offset outstr
ENDM
LBUF=82
EXTRN _gets:near
.MODEL small,C
.DATA
string db LBUF dup(?)
.STACK 100h
.CODE
beg proc far
.STARTUP
gets string
.EXIT
beg ENDP
END beg
Приведенная ниже программа показывает оформление вызываемого модуля, использующее Си- соглашения.
;----------------------------------------------------------------------------------------------------
;gets2 .asm
; Функция чтения строки. Прототип:
; int gets(char* p_str);
; По адресу p_str помещается строка в ASCIIZ. Функция возвращает число ;прочитаных символов.
;----------------------------------------------------------------------------------------------------
L_VAL=134
.MODEL small,C
.CODE
PUBLIC gets
gets PROC near
ARG p_str:word
LOCAL string:byte:L_VAL,p_ds:word
mov ax,bp
sub ax,L_VAL
push ds
push dx
mov p_ds,ds
mov dx,ax
mov ax,ss
mov ds,ax
mov string,L_VAL
mov ah,0ah
int 21h
push si
push di
mov si,dx
add si,2
mov ax,p_ds
mov es,ax
mov di,p_str
a: lodsb
cmp al,0dh
jz b
stosb
jmp a
b: xor al,al
stosb
xor ah,ah
mov al,string+1
pop di
pop si
pop dx
pop ds
ret
gets endp
end
Рис. 1.17. Пример оформления вызываемого программного модуля, использующего Си-соглашения.
Обратите внимание на код начала и конца модуля!!. Определите различие в кодах модулей gets1.asm и gets2.asm.
Глава 2. Вывод информации на терминал в текстовом режиме Вывод символьной строки. Модуль puts.
Функция puts предназначена для вывода символьной строки на экран в текстовом режиме работы видеоадаптера. При вызове этой функции необходимо в качестве параметра передать адрес выводимой строки. При этом используются соглашения, принятые для языка Си. В функции предусмотрена возможность перехода на следующую строку. Для этого в выводимой строке необходимо включить комбинацию символов “\n”, точно также как это делается в функции printf языка Си.
Блок-схема программы представлена на рис. 2. 1.
Число строк и столбцов можно определить из области данных BIOS для EGA/VGA, которая находится в фиксированной области памяти, расположенной в диапазоне адресов от 0000:0410 до 0000:0500. Слово по адресу 0000:044a содержит число символов в строке -1, а байт 0000:0484 число текстовых строк-1.
Для получения номера текущей активной страницы используем функцию 0fh прерывания int 10h. Эта функция позволяет определить номер
текущего режима работы видеоадаптера, номер активной страницы и количество символов в строке экрана. Формат функции имеет следующий вид:
На входе: ah= 0fh- определение текущего режима видеоадаптера.
На выходе: ah= число символов в строке;
al = номер текущего режима;
bh= номер активной страницы видеопамяти.
Рис. 2.1. Блок-схема программы puts.
При выводе часто необходимо управлять положением курсора. Для этого надо определить его текущее положение. Для этого можно воспользоваться функцией 03h BIOS. Функция имеет следующий формат:
На входе: ah = 03h;
bh = номер страницы видеопамяти.
На выходе: ch = верхняя граница курсора;
cl = нижняя граница курсора;
dh = позиция текущей строки курсора;
dl = позиция текущего столбца курсора.
В приведенной программе для определения координат курсора используется функция 03h прерывания int 10h.
При обнаружении комбинации “\n” реализуется переход на следующую строку. Для этого используется функция 02h BIOS. Эта функция имеет формат:
На входе: ah = 02h;
bh = номер страницы видеопамяти;
dh = номер строки (0 - 24);
dl = номер столбца (0 - 79).
На выходе: Не используется.
Предварительно определяется наличие свободной строки на экране дисплея. Если свободного места нет, проводиться свертка текстового окна вверх. Для той цели используется функция 06h BIOS, имеющей формат:
На входе: ah = 06h;
al = число, прокручиваемых линий; если оно равно нулю, то окно прокручивается целиком.
bh = атрибут для строк, возникающих снизу окна;
ch = номер строки верхнего левого угла окна;
cl = номер столбца верхнего левого угла окна;
dh = номер строки нижнего правого угла окна;
dl = номер столбца нижнего правого угла окна.
На выходе: Не используется.
В программе для вывода символов на экран использована функция 0ah прерывания int 10h, которая записывает символ в текущей позиции курсора. При этом атрибуты символа не задаются, а используются их старые значения из предыдущих операций записи. После операций записи положение курсора не изменяется. Управляющие символы, такие, как возврат каретки и перевод строки, не действуют и записываются как обычные символы. В связи с этим после вывода символа следует, используя функцию 02h прерывания int 10h, переместить курсор на следующую позицию. Функция вывода символа имеет формат :
На входе: ah = 0ah - запись символа без атрибута;
al = ASCII - код записываемого символа;
bh = номер страницы видеопамяти;
bl = цвет (для графических режимов);
cx = число записываемых символов (повторений).
На выходе: Не используются.
Подробный анализ работы программы можно провести на основании листинга, приведенного ниже. Эта программа реализует алгоритм, описание которого было приведено при описании блок - схемы программы.
.Model small,C
.CODE
Public puts
puts proc near
ARG p_string:word
LOCAL max_string:word,max_stolb:word
;Определяем число строк и столбцов в развертке
mov ax,40h
mov es,ax
xor ah,ah
mov al,byte ptr es:[84h]
mov max_string,ax
mov ax,es:[4ah]
dec ax
mov max_stolb,ax
;Определяем текущую активную страницу экрана
push si
push bx
push dx
push cx
push di
xor di,di
mov si,p_string
mov ah,0fh
int 10h ;номер активной страницы в регистре bh
;Получаем в dx координаты курсора
mov ah,3
int 10h
;В цикле выводим на экран символы строки
nex_char:
lodsb
cmp al,0 ;находим конец строки
je exit_proc ;если конец достигнут,
;то оканчиваем процедуру
cmp al,’\’
jne m
inc di
jmp nex_char
m: cmp di,1
jne m1
xor di,di
cmp al,’n’
jne m1
;перемещение курсора на следующую строку
inc dh
cmp dh,byte ptr max_string
jle m2 ;следующая строка помещается на экране
push ax
push bx
mov ah,06h
mov al,1
mov bh,07h
xor cx,cx
mov dh,byte ptr max_string
mov dl,byte ptr max_stolb
int 10h
pop bx
pop ax
m2: mov ah,02h
xor dl,dl
int 10h
jmp nex_char
m1:
mov ah,0ah
mov cx,1 ;символы строки выводятся без повтора
int 10h
;Перемещаем курсор на следующую позицию
mov ah,2
inc dl
int 10h
jmp nex_char
exit_proc:
pop di
pop cx
pop dx
pop bx
pop si
ret
puts ENDP
END
Рис. 2.2. Листинг программы puts.
;Запись строки символов. Эта пограмма в файле put_str.asm
EXTRN _puts:near
.MODEL small,C
.DATA
message db '\nint 10h\n',0
massage1 db 'Пример применения функции puts\n',0
.STACK 100h
.CODE
beg proc far
push ds
sub ax,ax
push ax
mov ax,@data
mov ds,ax
;Отображаем на активной странице строку message.
;Такой вызов корректен при использования соглашения языка Си.
call _puts, offset message
;-----------------------------------------------------------
;Это другой вариант вызова функции _puts. При этом отображается ;содержимое строки message1.
mov ax, offset message1
push ax
call _puts
add sp,2
;-------------------------------------------------------------
ret
beg ENDP
END beg
Рис. 2.3. На этом рисунке представлена программа put_str, которая является вызывающей для puts.
Приведенные выше функции gets и puts позволяют создать простейший диалог между пользователем и компьютером. Однако, для того чтобы облегчить отладку программы ,желательно иметь процедуру подобную функции getc языка Си. Листинг такой функции приведен на рис. 2.4.
;getc.asm . Ввод с ожиданием с клавиатуры без эха.
; char getc();
;Код прочитанного символа возвращается в al
;-------------------------------------------------------------------
.MODEL small,C
.CODE
PUBLIC getc
getc proc near
mov ah,08h
int 21h
ret
getc ENDP
END
Рис. 2.4. Ввод символа с клавиатуры.
;Пример диалога с пользователем программа put_get6.asm.
puts MACRO tex_str
LOCAL @met
.DATA
@met db tex_str,0
.CODE
call _puts,offset @met
ENDM
getc MACRO
puts '\n'
call _getc
ENDM
put MACRO p_str
call _puts,offset p_str
ENDM
gets MACRO outstr
call _gets, offset outstr
ENDM
EXTRN _gets:near, _puts:near,_getc:near
L_STROKA=82
.MODEL small,C
.DATA
message db '\n Начинаем диалог!!\n',0
stroka db L_STROKA dup(?)
.STACK 1024
.CODE
beg proc far
push ds
sub ax,ax
push ax
mov ax,@data
mov ds,ax
;Отображаем на активной странице экрана строку message
b: puts '\n Введите строку\n'
gets stroka
puts '\n Печать введенной строки\n'
put stroka
getc
cmp al,'q'
je a
jmp b
a:
puts '\n Конец сеанса.'
getc
ret
beg ENDP
END beg
Рис. 2.5. Пример организации диалога. Программа put_get6.asm.
В программе представленной на рис. 2.5 для вызова подпрограмм широко используются макросы, которые определены в начале программы. Применение макросов позволяет значительно сократить описание вызовов необходимых процедур. Это особенно заметно, если все макросы включить в отдельный заголовочный файл. Этот файл обычно имеет расширение *.h. В случае использование макросов достаточно воспользоваться директивой include c именем заголовочного файла. По мере разработки новых макросов их можно включать в этот же заголовочный файл. Назовем наш заголовочный файл именем stud_lab.h. Включим в этот файл все наши макросы и дополнительно элементы, которые являются общими для многих приложений. На рис. 2.6 представлен заголовочный файл stud_lab.h. В дальнейшем этот файл мы будем расширять.
puts MACRO tex_str
LOCAL @met
.DATA
@met db tex_str,0
.CODE
call _puts,offset @met
ENDM
getc MACRO
puts '\n'
call _getc
ENDM
put MACRO p_str
call _puts,offset p_str
ENDM
gets MACRO outstr
call _gets, offset outstr
ENDM
EXTRN _gets:near, _puts:near,_getc:near
Рис. 2.6. Заголовочный файл stud_lab.h.
Теперь программа put_get6.asm будет выглядеть следующим образом:
include stud_lab.h
L_STROKA=82
.MODEL small,C
.DATA
message db '\n Начинаем диалог!!\n',0
stroka db L_STROKA dup(?)
.STACK 1024
.CODE
beg proc far
push ds
sub ax,ax
push ax
mov ax,@data
mov ds,ax
;Отображаем на активной странице экрана строку message
b: puts '\n Введите строку\n'
gets stroka
puts '\n Печать введенной строки\n'
put stroka
getc
cmp al,'q'
je a
jmp b
a:
puts '\n Конец сеанса.'
getc
ret
beg ENDP
END beg
Назовем эту программу put_get7.asm.
