Информатика
.pdf151
N Å старшая половина Address
да |
нет |
|
N=00 |
N Å N – 1
Address Å N00
Sector Å
сектор N
Вывод
Sector
N-номер текущего сектора
Рис. 45. Алгоритм процедуры Prev_sector
13.4.Лабораторная работа 11
Вы п о л н и т е отладку процедур, предназначенных для переписки сектора. Для этого в начале файла Disp_sec.asm временно поместите тестовую процедуру Imit:
Imit:
call |
Init_sector |
|
mov |
ah,1 |
; Ожидание нажатия |
int |
21h |
; клавиши |
call N_sector
152
mov |
ah,1 |
; |
--//-- |
int |
21h |
|
|
call |
Prev_sector |
|
|
mov |
ah,1 |
; |
--//-- |
int |
21h |
|
|
call |
N_sector |
|
|
mov |
ah,1 |
; |
--//-- |
int |
21h |
|
|
call |
Next_sector |
|
|
mov |
ah,1 |
; |
--//-- |
int |
21h |
|
|
int |
20h |
|
|
Перевод строки
Вывод
приглашения
Ввод N
Address Å N00
Sector Å сектор N
Вывод
Sector
N – номер сектора (2-х значное шестнадцатеричное число)
Рис. 46. Алгоритм процедуры N_sector
153
Применение двойного вызова процедуры N_sector преследует цель создать требуемые условия для тестирования процедур Prev_sector и Next_sector, так как позволяет сделать текущим любой интересующий нас сектор. Особое внимание следует уделить граничным условиям – первому и последнему секторам.
14.ДИСПЕТЧЕР КОМАНД
Вданном разделе решается задача разработки диспетчера – программного модуля, обеспечивающего диалог с пользователем и выполняющего координацию работы других модулей программы (редактора). Применяемый при этом подход может быть использован для построения диспетчеров в других программных системах.
14.1.Ввод команд
Почти любая полноценная программа является интерактивной, т.е. способной вести диалог со своим пользователем. Естественно, что наш редактор информации также должен быть интерактивным. Он должен воспринимать команды пользователя, набираемые на клавиатуре и выводить на экран ответные сообщения. При этом принято называть сообщения пользователя (например команды) входными сообщениями, а сообщения программы – выходными.
Распознавание команд пользователя поручим программному модулю (процедуре), называемому диспетчером команд. Данный модуль занимает центральное место в программной системе и координирует работу других модулей. Допустим, что наш редактор выполняет всего шесть команд, каждой из которых соответствует своя управляющая клавиша:
<F1> – вывести на экран начальный сектор (256 байт) ОП; <F2> – записать скорректированный сектор в память;
154
<F3> – вывести на экран следующий сектор ОП;
<F5> – вывести на экран предыдущий сектор ОП;
<F6> – вывести на экран сектор N, где 0<=N<=256; <F10> – закончить работу редактора.
Клавиша <F4> оставлена «в резерве» для реализации нами в будущем функции редактирования.
Как говорилось ранее, каждому выводимому на экран символу соответствует свой код ASCII. Но каждому из этих символов соответствует также свой скан–код BIOS. Добавление слова BIOS требуется потому, что, вообще то, говоря, существуют несколько скан-кодов. В данном случае речь идет о том скан-коде клавиши, который возвращает BIOS в вызвавшую его прикладную программу. Далее, говоря «скан-код», мы будем подразумевать скан-код BIOS.
Грубо говоря, скан-код – номер нажатой клавиши. Но любой пользователь знает, что большинству клавиш соответствуют не один, а два или более символов. Поэтому один и тот же скан-код может соответствовать нескольким символам. Например, скан-код 16h соответствует четырем символам: u, U, г, Г. Поэтому для идентификации алфавитно-цифровых символов в обрабатывающих программах скан-коды не используются. Другое дело – управляющие символы, так как каждому из них соответствует своя отдельная клавиша.
Для того чтобы получить одновременно и ASCII-код клавиши, и ее скан-код, следует использовать системный вызов BIOS – “int 16h”, функция 0, выполняющий ввод символа с клавиатуры. После выполнения данного вызова в регистре AL содержится ASCII-код нажатой клавиши, а в регистре AH – ее сканкод.
Что касается управляющих клавиш, то они используются не для отображения на экране, а для управления работой программ. Многим из этих клавиш, в том числе и для <Fi>, соответствует один и тот же ASCII-код – 00h. Поэтому различить такие клавиши между собой можно лишь по их скан-коду:
|
|
|
|
155 |
3Bh – <F1>, |
3Ch - <F2>, |
3Dh - <F3>, |
3Eh - <F4>, |
|
3Fh - <F5>, |
40h |
- <F6>, |
41h - <F7>, |
42h - <F8>, |
43h - <F9>, |
44h |
- <F10>. |
|
|
Ниже приведен |
текст |
процедуры |
Read_byte, |
выполняющей ввод с |
клавиатуры любого символа – обычного или управляющего. При этом код ASCII символа возвращается в регистре AL, а в регистре АН возвращается соответствующий скан-код. Обратите внимание, что вывод “эха” символа не производится.
; Считывает код символа с клавиатуры
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;Выход: AL – код ASCII символа (0 – управляющий символ)
; |
АН – скан-код символа |
|
|
; |
|
|
|
Read_byte: |
|
|
|
|
mov |
ah,0 |
; Ввод символа |
|
int |
16h |
; без эха |
|
ret |
|
|
П о м е с т и т е процедуру Read_byte в начало файла Kbd_io.asm, временно записав перед ней оператор “org 100h”, а затем выполните ее отладку, используя Debug. Для этого получите файл Kbd_io.com и наберите команду
DOS:
DEBUG Kbd_io.com
Для запуска программы используйте команду Debug: “G i” , где i – адрес в листинге команды ret. Нажимая различные клавиши, проверьте правильность заполнения процедурой регистра АХ.
14.2. Алгоритм диспетчера
На рис.47 приведена блок-схема главной подпрограммы (процедуры) Dispatcher, выполняющей совместно с рассматриваемой далее процедурой
156
Command функции диспетчера команд. Для того чтобы обеспечить структурность алгоритма, мы, как и в одном из предыдущих разделов, используем флаг переноса CF и операции над ним.
|
|
|
Очистка экрана |
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Вывод |
|
|
|
|
|
|
|
|
|
|||
|
|
|
приглашения |
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Перевод |
строки |
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ввод символа |
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
нет |
|
|
|
|
|
да |
|
|
|
|
|
||
|
|
Символ |
|
|
|
|
|
|
|||||||
|
|
|
управл-й |
|
|
|
|
|
|
|
|
|
|||
|
|
|
нет |
<F10> |
да |
||||||||||
|
|
|
|
|
|
|
|||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
Выполнение |
|
|
|
|
|
|
|||||
CF Å 0 |
|
|
|
|
|
|
|||||||||
|
|
|
|
|
команды |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CF Å 1 |
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CF Å 0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
нет
CF = 1
да
CF – признак завершения (0 – продолжить, 1 – окончить работу)
Рис.47. Алгоритм процедуры Dispatcher
157
Кодирование процедуры Dispatcher не представляет особого труда. Многие этапы ее алгоритма реализуются путем вызова ранее разработанных процедур. Этап «Вывод приглашения» реализуется путем вывода на экран строки символов или, даже, всего одного-двух символов, однозначно указывающих на то, что редактор ожидает команд пользователя. Рассмотрим реализацию этапа «Выполнение команды».
14.3. Выполнение команды
Этап “Выполнение команды” реализуется путем вызова процедуры Command. Данная процедура получает в регистре DL код ASCII, соответствующий команде, и в зависимости от этого кода вызывает процедуру, выполняющую заданное командой действие.
На рис.48 приведена блок-схема процедуры Command. Центральной особенностью ее алгоритма является использование таблицы переходов. В данной таблице для каждой разрешенной команды пользователя отводятся три байта. В первом байте находится скан-код, соответствующий команде, а в двух других байтах – начальный адрес (внутрисегментное смещение) соответствующей процедуры. В последнем байте таблицы находится 0ffh.
Процедура Command совместно с главной процедурой Dispatcher обеспечивают выполнение алгоритма диспетчера команд. Поэтому обе процедуры, а также таблицу переходов Table мы поместим в один и тот же файл Dispatch.asm. Заключительный фрагмент этого файла содержит модули
Command и Table: org 100h
;Координирует выполнение модулей редактора
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Dispatcher:
. . . . . . . . |
; Тело главной процедуры |
158
int 20h
;
;Интерпретатор команд
;- - - - - - - - - - - - - - - - -
;Входы: DL – скан-код, соответствующий команде
;Чтение: Table – таблица переходов
; |
|
|
|
|
|
Command: |
|
|
|
|
|
|
push |
bx |
|
|
|
|
mov |
bx,Table |
|
; ВХÅадрес таблицы |
|
.M1: |
cmp |
byte[bx], 0ffh |
; Конец таблицы ? |
||
|
je |
.Exit |
|
; Да, кода нет в таблице |
|
|
cmp |
dl,[bx] |
|
; Это вход в таблицу? |
|
|
je |
.Dispatch |
|
; Да, выполнить команду |
|
|
add |
bx,3 |
|
; Нет, переход к |
|
|
clc |
|
|
; |
следующему |
|
jmp |
.M2 |
|
; |
элементу таблицы |
.Dispatch: |
inc |
bx |
|
; Адрес процедуры |
|
|
call |
[bx] |
|
; Вызов процедуры |
|
.Exit: |
stc |
|
|
; |
CF Å 1 |
.M2: |
jnc |
.M1 |
;Повторение для нового элемента таблицы |
||
|
pop |
bx |
|
|
|
|
ret |
|
|
|
|
;Таблица содержит скан-коды управляющих клавиш и адреса процедур,
;выполняющих соответствующие команды
Table |
db |
3bh |
; <F1> |
|
dw |
Init_sector |
|
|
db |
3ch |
; <F2> |
|
dw |
Write_sector |
|
db 3dh |
; <F3> |
159
dw |
Next_sector |
|
db |
3fh |
; <F5> |
dw |
Prev_sector |
|
db |
40h |
; <F6> |
dw |
N_sector |
|
db |
0ffh |
; Конец таблицы |
Выделение первого элемента таблицы
0ffh |
|
DL |
Код |
Другое значение |
||||||||||
|
|
|
|
|
|
|
|
элемента= |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Выделение |
|||
|
|
|
|
Вызов |
|
|||||||||
|
|
|
|
|
|
следующего |
||||||||
|
|
|
исполнителя |
|
|
|||||||||
|
|
|
|
|
|
элемента |
||||||||
CF Å 1 |
|
|
команд |
|
|
|
||||||||
|
|
|
|
|
таблицы |
|||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CF Å 1 |
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
CF Å 0 |
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CF = 1 |
нет |
|
|
да |
|
DL – код команды
CF – признак завершения (0 – продолжить, 1 – завершить работу)
Рис. 48. Алгоритм процедуры Command
Рассмотрим особенности этих модулей. Во-первых, сравним между собой два оператора: “cmp byte [bx], 0” и “cmp dl, [bx]”. Оба этих оператора
160
выполняют сравнение заданной величины (число 0 или содержимое регистра dl) с содержимым той ячейки памяти, адрес которой находится в регистре bx. В данном регистре находится адрес-смещение младшего байта ячейки памяти, но не задана длина этой ячейки. Об этой длине транслятор может узнать или из анализа второго операнда команды, или если задан дополнительный псевдооператор byte, word или dword. В первой из двух записанных выше команд псевдооператор byte сообщает транслятору, что сравниваются байты. Аналогично, команда “cmp word [bx], 0” будет сравнивать слова. Во второй из приведенных выше команд дополнительный псевдооператор излишен, так как о байтовой длине ячейки памяти сообщает другой операнд команды – регистр dl.
Для того чтобы получить адреса процедур, вызываемых диспетчером и содержащихся в таблице, мы записали имя каждой процедуры в качестве операнда псевдооператора dw, выполняющего запрос к транслятору с просьбой выполнить резервирование слова памяти и поместить в это слово требуемое значение. В данном случае таким требуемым значением является адрессмещение первого байта процедуры. Транслятор выполнит нашу просьбу правильно, так как имя процедуры записано без квадратных скобок, что и означает ее адрес.
Применение для реализации диспетчера команд таблицы переходов значительно увеличивает его гибкость, т.е. пригодность к модификации. В самом деле, мы легко можем добавить в нашу программу новые команды, набираемые на клавиатуре – достаточно записать процедуру, выполняющую новую команду, в подходящий файл, и поместить новую точку входа в Table.
14.4.Лабораторная работа 12
За п и ш и т е файл Dispatch.asm на диск и выполните отладку программы. Отлаженная программа должна правильно реагировать на нажатие любой клавиши.