10.6. Вставка короткой программы

В предыдущем примере рассматривалась довольно большая программа на языке ассемблера, хранящаяся в собственном объектном файле и загружаемая в память интерпретатором Бейсика. А как в случае очень маленькой программы. Представляется, что для такой программы тратилось бы слишком много усилий на одну только загрузку ее из собственного файйла. В приложении C справочника по языку Бейсик приведен способ "упаковки" программы на машинном языке в область памяти за пределами рабочей области интерпретатора. Приведем пример применения другого способа.

На Фиг. 10.8 показана программа, написанная на языке ассемблера, которой мы воспользуемся. Эта программа обращается к BIOS для сдвига изображения на экране. Рассмотрев параметры, хранящиеся в регистрах CX и DX, можно увидеть, что сдвигаемое окно отображает лишь часть экрана. Мы будем исползовать приведенную программу для разбиения экрана на несколько окон, в каждом из которых сдвиг может производиться независимо. Поскольку средства реализации этого в языке Бейсик отсутствуют, понадобится процедура на языке ассемблера.


Microsoft (R) Macro Assembler Version 5.00                  1/1/80 04:03:56
Фиг. 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. В программу на Бэйсике эта программа вводится с клавиатуры по листингу ассемблирования. Это - одна из причин, по которой применение рассмотренного способа ограничено лишь короткими программами. При вводе программы таким способом очень легко сделать ошибки.


  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 можно использовать для трассировки программы на машинном языке. Конечно, если программа на языке ассемблера хорошо написана и коротка, то такая отладка не так необходима. На самом же деле вы, вероятно, заметите, что в большинстве случаев из-за ошибок при вводе с клавиатуры программы на машинном языке в строку интерпретатора Бейсик возникает множество проблем.