В предыдущем примере рассматривалась довольно большая программа
на языке ассемблера, хранящаяся в собственном объектном файле и
загружаемая в память интерпретатором Бейсика. А как в случае очень
маленькой программы. Представляется, что для такой программы
тратилось бы слишком много усилий на одну только загрузку ее из
собственного файйла. В приложении C справочника по языку Бейсик
приведен способ "упаковки" программы на машинном языке в область
памяти за пределами рабочей области интерпретатора. Приведем пример
применения другого способа.
На Фиг. 10.8 показана программа, написанная на языке
ассемблера, которой мы воспользуемся. Эта программа обращается к
BIOS для сдвига изображения на экране. Рассмотрев параметры,
хранящиеся в регистрах CX и DX, можно увидеть, что сдвигаемое окно
отображает лишь часть экрана. Мы будем исползовать приведенную
программу для разбиения экрана на несколько окон, в каждом из
которых сдвиг может производиться независимо. Поскольку средства
реализации этого в языке Бейсик отсутствуют, понадобится процедура
на языке ассемблера.
Microsoft (R) Macro Assembler Version 5.00 1/1/80 04:07:03
Фиг. 10.8 Программа прокрутки окон на дисплее Page 1-1
PAGE ,132
TITLE Фиг. 10.8 Программа прокрутки окон на дисплее
0000 CODE SEGMENT
ASSUME CS:CODE
0000 SCROLL PROC FAR
0000 55 PUSH BP
0001 8B EC MOV BP, SP
0003 8B 76 06 MOV SI, [BP+6] ; Загрузка адреса параметра
0006 8B 0C MOV CX, [SI] ; Загрузка параметра
0008 0A C0 OR AL, AL
000A B7 07 MOV BH, 7
000C B8 0601 MOV AX, 601h
000F 75 0C JNZ WINDOW1 ; Определение требуемого окна
0011 B9 0200 MOV CX, 200h ; Окно 1
0014 BA 1010 MOV DX, 1010h
0017 DO_SCROLL:
0017 CD 10 INT 10h
0019 5D POP BP
001A CA 0002 RET 2
001D WINDOW1:
001D B9 0514 MOV CX, 514h ; Окно 2
0020 BA 1224 MOV DX, 1224h
0023 EB F2 JMP DO_SCROLL
0025 SCROLL ENDP
0025 CODE ENDS
END
Фиг. 10.8 Процедура сдвига изображения для Бэйсика
Как можно увидеть на листинге ассемблирования на Фиг. 10.8, для
определения, в каком из двух окон должен производиться сдвиг,
используется входной параметр. Программа, написанная на языке
Бейсик, передает этот параметр в ассемблерную подпрограмму
оператором CALL. На Фиг.10.9(а) показано содержимое стека в момент
вызова в Бэйсике процедуры SCROLL. Оператор CALL помещает в стек
адрес параметра перед выполнением дальнего вызова (FAR CALL)
подпрограммы на машинном языке. Адрес в стеке является смещением
параметра относительно регистра DS. Первые команды процедуры SCROLL
извлекают этот адрес из регистра SI для того, чтобы загрузить
истинное значение в регистр CX. На Фиг.10.9(b) показано содержимое
стека после того, как процедура SCROLL поместила содержимое
регистра BP в стек, а затем переслала содержимое регистра SP в
регистр BP. Обратите внимание, что параметр находится в шести
байтах от вершины стека. Если бы программа на языке Бейсик
передавала более одного параметра, перед вызовом они были бы
аналогичным образом помещены в стек. Забегая вперед, заметим, что
перед возвратом процедура, используя команду RET 2, извлекает
параметры из стека. Интерпретатор Бейсика предполагает, что перед
возвратом подпрограмма удаляет параметры из стека.
ГДДДДДДДДДДДДґ ГДДДДДДДДДДДДґ
SPДДДДД>і Смещение і SPДДДД>і Старое зна-і
і возврата і і чение BP і<ДДДДBP
ГДДДДДДДДДДДДґ ГДДДДДДДДДДДДґ
і Сегмент і і Смещение і [BP+2]
і возврата і і возврата і
ГДДДДДДДДДДДДґ ГДДДДДДДДДДДДґ
і Смещение і і Сегмент і [BP+4]
і аргумента і і возврата і
ГДДДДДДДДДДДДґ ГДДДДДДДДДДДДґ
і Смещение і [BP+6]
і аргумента і
ГДДДДДДДДДДДДґ
(a) (b)
Фиг. 10.9 Стек при вызове процедуры
Подпрограмма SCROLL в зависимости от значения параметра
обрабатывает одно из двух окон экрана. Если параметр равен нулю,
то изображение в окне, заданном координатами (2, 0) и (16, 16)
сдвигается вверх на одну строку. Если параметр не равен нулю, то
на одну строку вверх сдвигается изображение в окне (5, 20), (18,
36). Перемещается текст только в заданном окне, остальной текст
или данные на экране остаются неподвижными. Реализация такого
оконного режима входит в функцию сдвига BIOS. Для ее использования
требуется лишь вызвать BIOS с правильно заданными параметрами.
На Фиг. 10.10 представлена программа на языке Бейсик,
обращающаяся к процедуре SCROLL. В этом простом примере в каждое
окно записывается строка символов, а затем вызывается процедура
сдвига текста вверх. Эта Бэйсик-программа не более чем
иллюстрирует использование сдвига окон.
Первое, на что следует обратить внимание, это - способ загрузки
программы на машинном языке в систему. Программа содержится в
символьной строке P$. Каждый символ в строке соответствует одному
байту объектного кода из Фиг. 10.8. В программу на Бэйсике эта
программа вводится с клавиатуры по листингу ассемблирования. Это -
одна из причин, по которой применение рассмотренного способа
ограничено лишь короткими программами. При вводе программы таким
способом очень легко сделать ошибки.
A
1 CLS
5 DEFINT A-Z
10 P$=CHR$(&H55)+CHR$(&H8B)+CHR$(&HEC)+CHR$(&H8B)+CHR$(&H76)+CHR$(&H6)
20 P$=P$+CHR$(&H8B)+CHR$(&HC)+CHR$(&HA)+CHR$(&HC9)+CHR$(&HB7)+CHR$(&H7)
30 P$=P$+CHR$(&HB8)+CHR$(&H1)+CHR$(&H6)+CHR$(&H75)+CHR$(&HC)+CHR$(&HB9)
40 P$=P$+CHR$(&H0)+CHR$(&H2)+CHR$(&HBA)+CHR$(&H10)+CHR$(&H10)+CHR$(&HCD)
50 P$=P$+CHR$(&H10)+CHR$(&H5D)+CHR$(&HCA)+CHR$(&H2)
60 P$=P$+CHR$(&H0)+CHR$(&HB9)+CHR$(&H14)
70 P$=P$+CHR$(&H5)+CHR$(&HBA)+CHR$(&H24)+CHR$(&H12)+CHR$(&HEB)+CHR$(&HF2)
100 ENTRY!=(PEEK(VARPTR(P$)+1))+(PEEK(VARPTR(P$)+2))*256
110 IF ENTRY!>32768! THEN ENTRY%=ENTRY!-65536! ELSE ENTRY%=ENTRY!
120 A$="АБВГДЕЖЗИК"
130 L=0:R=1
140 LOCATE 1,1:PRINT "Пример сдвига окна . . .э
200 LOCATE 15,1:PRINT A$;
210 CALL ENTRY%(L)
220 LOCATE 18,21:PRINT A$;
230 CALL ENTRY%(R)
240 A$=RIGHT$(A$,9)+LEFT$(A$,1)
250 GOTO 200
Фиг. 10.10 Бэйсик-программа для сдвига окон
Поскольку программа на машинном языке задана в строке P$, то
для определения адреса этой строки программа на языке Бейсик
использует функцию VARPTR. Для оператора CALL необходим адрес
подпрограммы, поэтому для его нахождения и используется функция
VARPTR. Воспользовавшись информацией из приложения C справочника
по Бейсику, можно найти адрес строки во втором и третьем байтах
дескриптора строки. Возвращаемое функцей VARPTR значение является
адресом дескриптора строки для P$. Программа извлекает адрес
строки из дескриптора и присваивает его значение переменной ENTRY!.
Поскольку это значение может находиться в диапазоне от 0 до 65536,
подпрограмма должна преобразовать его в целое значение длиной в
одно слово, со значением от от -32768 до 32767. Это слово
помещается в переменную ENTRY%. В остальных строках программы в
сдвигаемые окна записывается символьная строка, а затем для
перемещения текста вызывается подпрограмма SCROLL.
При запуске этой программы вы увидите, что данные в двух окнах
перемещаются независимо. Такой прием позволяет задать два
различных окна на экране и перемещать в них текст независимо друг
от друга. Если написать немного более длинную программу, можно
было бы ограничить каждое окно рамкой, чтобы деиствительно отделить
их друг от друга. Применение подобных методов построения окон
позволяет писать довольно симпатичные программы с одновременным
выводом на экран наскольких фрагментов текста.
Прежде чем покончить с этой программой, давайте просмотрим
через отладчик часть программы, написанную на машинном языке. Для
этого надо иметь готовую к выполнению программу ДОС DEBUG. Это
достигается следующим образом: сначала загружается программа
DEBUG, а затем загружается BASIC.COM (или BASICA.COM, если
используется расширенный Бейсик). После загрузки программы Бейсик
замените первый символ в P$ (и соответственно, первый байт
программы на машинном языке), на CHR$($HCC). Это - код для
прерывания INT 3 прерывания по точке прерывания. Теперь, когда во
время выполнения программы на языке Бейсик она вызывает
подпрограмму на машинном языке, управление получает программа
DEBUG. Теперь можно вновь заменить код 0CCH на исходное значение
(в данном случае 055H). Программу DEBUG можно использовать для
трассировки программы на машинном языке. Конечно, если программа
на языке ассемблера хорошо написана и коротка, то такая отладка не
так необходима. На самом же деле вы, вероятно, заметите, что в
большинстве случаев из-за ошибок при вводе с клавиатуры программы
на машинном языке в строку интерпретатора Бейсик возникает
множество проблем.