[an error occurred while processing this directive]

Раздел I 4.1

Создание модуля команд

Рассмотрев все основные модули "Системы расширения возможностей интерпретатора "Бейсик", мы подошли к настоящему разделу, посвященному проблеме создания модуля команд, состоящего из отдельных процедур и функций.

Создание собственных модулей команд по-существу и является основной задачей программиста, разрабатывающего новые драйверы на основе "Системы".

Прежде всего следует уяснить, что любая команда - это всего лишь обычная подпрограмма на машинном языке, выполняющая какие-либо действия, определяющие ее конкретное предназначение.

Для примера рассмотрим подпрограмму LINE, строящую горизонтальную линию на текстовом экране из псевдографических символов "-".

*-------------------*
* Подпрограмма LINE *
*-------------------*

вход: X -длина линии (0-255)

COUT3   EQU ¤FC0E  подпрограмма среды "Монитор"

LINE    CPX #0
        BEQ END
LINE1   JSR COUT3
        DEX
        BNE LINE1
END     RTS

Подпрограмма LINE является простейшей подпрограммой на языке Ассемблера. Она строит горизонтальную линию из символов "-", количество которых должно быть установлено в регистре X, пользуясь подпрограммой системы "Монитор" COUT3 (¤FC0E).

Ничего не стоит превратить эту подпрограмму в процедуру, доступную интурпретатору "Системы".

Для организации команды требуется знать формат "Поля команды" (особого заголовка, стоящего перед исполняемой подпрограммой команды и служащего для ее идентификации модулем интерпретации команд INTPRALL.ASM и связи команд в модули).

Общий формат поля команды:

         DFB ATRIBUT
         DW  NEXTCOM
         DFB MIN,MAX
         ASC "NAME comentary"
         DFB 0

-1-й байт - атрибут команды в битах (ATRIBUT)

Номер бита:!Значение:
-----------+------------------------------------------------
    7      !"1"-функция,"0"-процедура
    6      !"1"-нестандартная интерпретация параметров,
           !"0"-обычная
    5      !"1"-команда запрещена в режиме диалога,
           !"0"-разрешена
    4      !"1"-результат функции-строка,"0"-число 
           !(значим только для функций).
   3-0     !резерв (в интерпретаторе INTPRALL.ASM не исполь-
           !зуются)

-2-3-й байты - ссылка на следующую команду (фактически должны содержать адрес начала поля следующей команды). Если данная команда последняя в списке, то ссылки нет (нулевые байты 2-3).

-4-5-й байты - соответственно минимальное и максимальное количество входных параметров (MIN,MAX). Минимальное их кол-во, естественно, 0, максимальное 20 (для INTPRET.ASM - 32).

-NAME (имя команды). Имя команды должно быть записано заглавными буквами и не содержать пробелов. Длина имени не более 15 символов.

-comentary (коментарий). После имени команды может следовать коментарий - произвольный текст длиной до 230 символов, отделенный от имени команды NAME пробелом.

Предпочтительно указание в коментарии формата параметров команды и их типов.

-0 (нулевой байт) - признак конца Поля команды. (если коментария нет, то он следует сразу за именем команды).

*Примечание:

В более ранней версии интерпретатора команд INTPRET.ASM (в отличие от описываемого INTPRALL.ASM) формат поля команды несколько отличался. Поля команд под INTPRET.ASM не содержали байта ATRIBUT (все команды считались процедурами).

Итак, теперь снабдим нашу подпрограмму LINE необходимым заголовком команды:

*-------------------*
* Процедура  "LINE" *
*-------------------*

LINE    DFB :00000000
        DW NEXTCOM
        DFB 0,1
        ASC "LINE X"
        DFB 0

Атрибут команды LINE указывает, что это процедура (7-й бит "0") с обычной интерпретацией параметров (6-й бит "0") и разрешена в диалоге (5-й бит "0").

Минимальное кол-во параметров - 0, максимальное - 1 (числовой, т.к. интурпретация обычная).

Теперь осталось связать нашу процедуру с ее полем и передать ей параметры из буфера параметров (их накапливает INTPRALL.ASM в BUF1 и BUF11; см. Раздел I 3.1.3)

*-------------------*
* Процедура  "LINE" *
*-------------------*

LINE    DFB :00000000
        DW NEXTCOM
        DFB 0,1
        ASC "LINE X"
        DFB 0

LIST1   LDX BUF1
        BEQ END
LIST2   JSR COUT3
        DEX
        BNE LIST2
END     RTS

У подпрограммы LINE всего лишь один параметр, следовательно он хранится в BUF1 (младший байт).

Однако параметр может быть не задан вовсе (минимальное кол-во параметров 0), следовательно, требуется это проверить:

*-------------------*
* Процедура  "LINE" *
*-------------------*

LINE    DFB :00000000
        DW NEXTCOM
        DFB 0,1
        ASC "LINE X"
        DFB 0

LINE1   LDX XBUF1
        BNE LINE2
        LDX #¤FF
        BNE LINE3
LINE2   LDX BUF1
        BEQ END
LINE3   JSR COUT3
        DEX
        BNE LINE3
END     RTS

Теперь процедура LINE строит линию из 255 символов, если параметров не задано (кол-во их хранится в XBUF1) или строит линию из того кол-ва знаков "-", которое передается параметрам X (берется из BUF1).

Однако следует вспомнить, что ширина экрана АЦР32 - 32 символа, а АЦР64 - 64 символа. Кроме того, необходимо проверить, чтобы старший байт параметра X был равен 0.

*-------------------*
*  Процедура "LINE" *
*-------------------*

LINE    DFB :00000000
        DW NEXTCOM
        DFB 0,1
        ASC "LINE X"
        DFB 0

LINE1   JSR CH.PARM
        LDX XBUF1
        BNE LINE3
        BIT COLOR
        BMI LINE2
        LDA LINE32
        BNE LINE4
LINE2   LDX LINE64
        BNE LINE4
LINE3   LDX BUF1
LINE4   JSR CHLINE
        BCS LINERR
LINE5   JSR COUT3
        DEX
        BNE LINE5
        RTS
CHLINE  BIT COLOR
        BMI CHLINE1
        CPX LINE32
        RTS
CHLINE1 CPX LINE64
        RTS
LINE32  DFB 32
LINE64  DFB 64
LINERR  JMP BASERR ; выдать "Ошибочное значение"

Теперь мы получили законченную, универсальную процедуру. В первой строке мы обращаемся к подпрограмме CH.PARM модуля ALLSUBRS.ASM (она проверяет, чтобы параметры имели значение не более 255; см.Раздел I 3.1.4). Еще мы дополнили нашу процедуру проверкой режима (он проверяется по состоянию COLOR; в АЦР64 COLOR ≥ ¤80) относительно которого выбирается максимальная длина линии (LINE32 или LINE64) и осуществляется проверка допустимого максимального значения параметра.

Как видите, создание процедуры под "Систему" не такое уж сложное дело. Автор постепенно написал всю процедуру вместе с данным описанием всего за 15 минут.

Конечно, эта процедура была написана только ради примера и не претендует на самое красивое решение. Более сложные команды должны создаваться после тщательного обдумывания и желательно, так, чтобы они максимально охватывали сферу своего применения. Их следует стараться писать максимально рационально, так как планируется многократное их использование в разных драйверах.

Теперь следует сказать несколько слов об особенностях создания функций.

Для примера рассмотрим функцию DEFSLOT библиотеки фирмы Friends' Software:

*-------------------*
* Функция "DEFSLOT" *
*-------------------*
; функция определяет устройство, установленное 
; в разъем S (тип контроллера)

DEFSLOT  DFB :10000000
         DW  NEXTCOM
         DFB 1,1
         ASC "DEFSLOT (S)"
         DFB 0

         JSR CH.PARH
         LDA BUF1     параметр S
         BEQ DSERR    не может быть равен 0
         CMP #1
         BEQ DSERR    в 1й слоте генплата
         CMP #7       не может быть больше 6
         BCS DSERR    (у АГАТа 6 слот!)
         ORA #¤C0
         STA RNDN     занести в RNDN
         LDA #¤0C
         STA RNDL     занести в RNDL
         LDY #0
         LDA (RNDL),Y считать байт, по адресу ¤CS0C
         TAY          поместить в регистр Y
         LDA #0       в аккумуляторе 0
         RTS
DSERR    JMP BASERR46 выдать ошибку "Ошибочное значение"

Функция DEFSLOT (7-й бит "1") возвращает константу-число (4-й бит "0"), определяющее тип контроллера, установленный в разъем с номером S (обязательный параметр).

DEFSLOT может возвращать следующие константы:

¤10 - ячейка памяти
¤4B - ячейка принтера
¤BD - контроллер дисковода FD55 (ЕС 5323.01)
¤F0 - контроллер дисковода ЕС 5088
¤EA - контроллер "сети".

Результат своей работы функция возвращает в регистрах:

X,Y,A. В данном случае возвращаемое число не выходит из диапазона 0-FF. Поэтому A=0, Y - содержит константу. Регистр X - значим только для функций, результат которых - строка.

В общем случае функция должна вернуть:

регистр X - длина текста (для числовых функций не используется)

A,Y - адрес хранения текста (для строк)
      A - старший байт |Возвращаемый текст должен быть в 
      Y - младший      |обратном коде (EOR #¤80)

    - число (для функций с числовым параметром):
A - старший байт
Y - младший.

В создании команд, требующих строковые параметры имеется еще одна тонкость. Дело в том, что стандартные интерпретаторы INTPRET.ASM или INTPRALL.ASM могут передавать командам только числовые параметры. Если Вам требуется передать хотя бы один строковый параметр команде, Вам придется воспользоваться так называемой "нестандартной интерпретацией параметров", которая разрешается для команды, если бит 6 ее атрибута установлен в "1".

Рассмотрим строковую функцию ZSTR¤, которая превращает переданную ей строку в строку, состоящую из заглавных символов:

*---------------*
* Функция ZSTR¤ *
*---------------*

        DFB :11110000
        DW  NEXTCOM
        DFB 0,0
        ASC "ZSTR¤ (A¤)"
        DFB 0

ZSTR1   SEC
        JSR INPAR
        TXA
        PHA
        LDY #0
ZSTR2   LDA (¤5E),Y
        ORA #¤80
        STA ¤200,Y
        INY
        DEX
        BNE ZSTR2
        JSR GETC1
        CMP #¤29  закрывающая скобка
        BNE ZSTRERR
        JSR GETCHR
        BEQ ZSTR3  конец строки
        CMP #¤3A   или двоеточие
        BNE ZSTRERR
ZSTR3   PLA
        TAX
        LDA #¤02
        LDY #0
        RTS
ZSTRERR JMP BASERR12

; Универсальная подпрограмма INPAR
; "Считать параметры"
; by Serkov S.V. 13.08.95
;--------------------------------------
; вход: а) C=0  требуется число
;       б) C=1  требуется строка
; выход а) C=0 
;          Y,A - число (младший/старший)
;       б) C=1
;          Y,A - адрес
;          X - длина строки

INPAR   PHP
        JSR ¤DD62
        PLP
        BCC NPUR
SPUR    JSR ¤E5BC
        TAX
        LDA ¤5F
        LDY ¤5E
        BIT ¤11
        BPL PARERR
        SEC
        RTS
NPUR    BIT ¤11
        BMI PARERR
        JSR ¤E712
        CLC
        LDA ¤51
        LDY ¤50
        RTS
PARERR  JMP BASERR (синтаксическая ошибка)

Интерпретатор INTPRALL.ASM не берет никаких параметров для команд с нестандартной интерпретацией и, пропустив последнюю кавычку имени команды, сразу переходит к ее исполнению (для процедур). Для фукций еще пропускается открывающая скобка "(". В нашем примере функция ZSTR¤(A¤) самостоятельно интерпретирует свой аргумент A¤ через универсальную подпрограмму INPAR (INPAR может работать как с числовыми, так и со строковыми параметрами). По окончанию работы функции (делает все символы заглавными, ORA #¤80, в регистре X возвращается ее длина, а в Y,A - адрес новой строки. Интерпретатор сам присвоит результат заданной переменной (если пользователь запишет:)

&B¤=ZSTR¤"(A¤)

Функция ZSTR¤ при интерпретации аргумента также проверяет налачие после аргумента A¤ закрывающей скобки (во внутреннем представлении Бейсика константа ¤29) и следующего за ней признака конца строки (0) или двоеточия (¤3A) отделяющего функцию от следующей команды Бейсика.

Для их интерпретации ZSTR¤ использует обращение к системным подпрограммам GETC1 и GETCHR.

Их значение следующее:

GETC1 - взять символ, на который указывает указатель текущего символа интерпретатора Бейсик (¤B8-¤B9).

GETCHR - пропустить текущий символ и взять следующий, обратившись к GETC1.

Для интерпретации могут оказаться полезными следующие знаки во внутреннем представлении Бейсика:

¤22 - кавычки
¤28 - открывающая скобка
¤29 - закрывающая скобка
¤3A - двоеточие
¤D0 - знак равенства
¤2C - запятая.

Теперь вернемся к подпрограмме INPAR. INPAR пользуется универсальной подпрограммой Бейсика READPAR (¤DD62).

Эта подпрограмма способна считывать любые параметры, представленные в переменными и выражениями в формате языка Бейсик, начиная от первого символа параметра, до первого символа к нему не относящегося. При этом, если выражение записано неверно, Бейсик может диагностировать различные типы ошибок.

Вообще подпрограмму INPAR Вы можете разобрать самостоятельно, к тому же, она снабжена необходимым кол-вом комментариев.

Автор рекомендует использовать указанную подпрограмму в своих модулях команд. (Подпрограмма создавалась для нового интерпретатора команд INTBASIC.ASM, выход которого планируется к 01.10.95).

Теперь, после стольких примеров, я думаю, что Вас не затруднит создание любой команды. Стоит сказать еще несколько слов об организации ошибок в командах и их обработке.

Для организации ошибок формата интерпретатора "Бейсик" (коды ошибок и их диагностика см. Раздел III.6 ) следует загрузить код соответствующей ошибки в регистр X и произвести переход на точку BASERR, определенную в модуле INTPRALL.ASM.

Например:

      LDX #130    выдать ошибку "Строка длинна"
      JMP BASERR  

для ошибок, обработка которых осуществляется модулем INTPRALL.ASM можно сразу делать JMP на их точки входа

Для диагностики ошибок ДОС (коды см. Раздел III.6) и ошибок с собственными диагностиками следует пользоваться услугами модуля универсальной обработки ошибок ERRORMOD.ASM (см. Раздел I 3.2.2).

.ст Раздел I:4.2

[an error occurred while processing this directive]