Страница Ивана Рощина > Статьи >

© Иван Рощин, Москва

ZXNet: 500:95/462.53
E-mail: bestview@mtu-net.ru
WWW: http://www.ivr.da.ru

Art Studio и принцип открытой архитектуры

(Adventurer #8,
 «Радиолюбитель. Ваш компьютер» 8/2000)
Дата последнего редактирования: 24.09.2004.

Есть такая версия графического редактора Art Studio (v2.01c, corrected by ROM/SM and FUX/SM), которая может проигрывать музыку во время работы. Т.е. на диске записан сам редактор и сразу же за ним — файл «artmusic», содержащий откомпилированную музыку в формате Pro Tracker 2 или Sound Tracker Pro. При запуске редактора этот файл загружается в свободный банк ОЗУ (1 или 6), и в процессе работы происходит вызов плеера по прерываниям. (Оказалось, что имя этого файла не имеет значения, лишь бы он был записан непосредственно за редактором, т.е. за файлом «ART <B>».)

Раньше я пользовался другими версиями Art Studio, у которых были свои недостатки: то стрелка исчезнет непонятно куда, то на экране появятся цветные полосы и всё зависнет… Так что с появлением новой версии я перешёл на неё. Но и тут меня поджидали неприятные особенности (в общем-то, известные всем пользователям Art Studio) — включение белого бордюра в режимах Magnify, Font editor и Scrapbook. Надо было что-то с этим делать, и я решил немного изменить плеер в файле с музыкой. Редактор устанавливал белый бордюр — а вызываемый по прерываниям плеер, помимо основной своей задачи, устанавливал нужный мне цвет бордюра.

Довольно долго я работал с такой версией Art Studio, пока не возникла другая проблема: нужно было нарисовать некоторое изображение по известным координатам его точек. Как, например, отметить на рисунке точку с координатами x=45, y=83? Вот если бы на экране постоянно отображались текущие координаты курсора…

И тут я подумал: а что если написать модуль с таким же стартовым адресом (#C000), такими же точками входа (INIT=#C000, PLAY=#C006), как и у откомпилированной музыки, а потом «подсунуть» его редактору, записав вместо файла «artmusic». А этот модуль, в свою очередь, каждый раз, когда к нему производится обращение, будет выводить на экран координаты курсора.

Модуль (известный под именем art_xy) был написан и успешно выводил координаты (а кроме того, следил за бордюром и устанавливал нужные мне начальные значения атрибутов). В конце этой статьи приведён исходный текст его главной части, который пригодится вам при разработке собственных модулей.

Что же мы видим? Оказывается, любую отсутствующую в Art Studio возможность нетрудно реализовать самому, написав для этого специальный модуль. А если написано несколько модулей, то можно загружать их по очереди, не выходя из редактора (для этого, естественно, в каждом модуле должна быть предусмотрена команда «загрузить другой модуль и передать ему управление»).


Создать свой модуль, без сомнения, намного легче, чем написать новый графический редактор «с нуля». Различные модули могут быть написаны разными людьми, независимо друг от друга. А в этом и заключается принцип открытой архитектуры. Наверняка James Hutchby, автор Art Studio, и не предполагал о таких возможностях своего редактора.

Какие функции могут содержаться в дополнительном модуле? Например, быстрый просмотр записанных на диске картинок без загрузки их в редактор, сохранение и восстановление конфигурации, использование дополнительной памяти (более 128 КБ) в качестве RAM-диска, выполнение различных преобразований над отмеченным окном, ну и, конечно, музыка и скринсейвер. Всё это вполне уместится в 16 КБ.

В ZX-Format #7 можно прочесть о проекте «STATE OF THE ART» группы AVALON. Как там написано, это новый графический редактор с необыкновенными возможностями, сочетающий достоинства лучших редакторов на ZX Spectrum, PC и AMiGA. Хотелось бы, чтобы и там использовался модульный принцип построения, с подробным описанием интерфейса между модулем и редактором, чтобы каждый пользователь мог сам реализовать нужную ему функцию. Это даёт практически неограниченное расширение возможностей редактора.

Теперь я более подробно расскажу о том, как написать собственный модуль для Art Studio. Начну с известных мне адресов, по которым находятся переменные редактора:

 ink    - 52186 (0-7, 8 - transparent)
 paper  - 52187 (0-7, 8 - transparent)
 border - 52188 (0-7)
 bright - 52189 (0 - off, 1 - on, 2 - transparent)
 flash  - 52190 (0 - off, 1 - on, 2 - transparent)

Абсолютные координаты курсора (x — 56035, y — 56036) дублируются по адресам 64977 и 64978 соответственно. Начало координат расположено в левом верхнем углу экрана.

Так как эти переменные находятся в 0 банке памяти, а модуль загружается в 1 или 6 банк, доступ к ним осуществляется с помощью специальных функций (см. листинг).

Во время первых 50 обращений к модулю (т.е. 1 секунда реального времени после запуска редактора) происходит распаковка редактора и инициализация переменных. Если в модуле предусмотрено изменение задаваемых по умолчанию атрибутов (ink, paper, border) на другие, то эти атрибуты нужно устанавливать заново при каждом из первых 50 обращений. Объяснение тут такое: очевидно, при запуске редактора выполняются команды следующего вида:

   LD A,0
   LD (52186),A  ;ink
   LD A,7
   LD (52187),A  ;paper
   LD A,7
   LD (52188),A  ;border

   ... ;Происходит прерывание, вызывается модуль, который
       ;устанавливает нужные ink, paper, border.

   CALL CLS      ;Очистка экрана
                 ;в соответствии с атрибутами.

Видно, что установка нужных атрибутов должна произойти после того, как редактор установит их значения, но до того, как редактор выполнит очистку экрана. А так как неизвестно, какое по счёту прерывание будет соответствовать этому моменту, приходится выполнять установку атрибутов 50 раз.

Кстати, если при запуске редактора нужно изменить заданные по умолчанию значения атрибутов на transparent, это лучше сделать после первых 50 вызовов модуля — иначе во время очистки экрана атрибуты знакомест установятся случайным образом.

Исходный текст главной части модуля art_xy

        ORG     #C000

        JP      INIT  ;#C000 - INIT
        DS      3
        JP      PLAY  ;#C006 - PLAY
        RET           ;#C009 - STOP

;Значения ink, paper, border:

COLORS  DB      7,0,0

;---------------------------------------
;Процедура INIT - выполняется перед
;началом работы редактора.

INIT    DI

;Сохраняем память под резидентом:

        CALL    SAVE_M

;Переносим резидент N3 и запускаем его.
;В ячейке BANK получаем номер банка,
;куда загружен art_xy:

        LD      HL,RES3_B
        CALL    LOAD_R
        CALL    #6000

;Восстанавливаем ранее сохранённый
;участок памяти под резидентом:

        CALL    LOAD_M

;Вывод сообщения "загружен модуль..."
;и ожидание нажатия клавиши:

        CALL    #D000
INIT1   XOR     A
        IN      A,(254)
        CPL
        AND     31
        JR      Z,INIT1
        EI
        RET

;---------------------------------------
;Процедура PLAY - выполняется каждые
;1/50 секунды.

PLAY    DI
        CALL    SAVE_M
        ...
        CALL    LOAD_M
        EI
        RET

;---------------------------------------
;Процедура LOAD_M сохраняет в буфере
;участок памяти под резидентом
;(адрес начала - #6000, длина участка -
;длина самого длинного резидента, в
;данном модуле это резидент N3).

LOAD_M  LD      HL,RES_BUF
LOAD_R  LD      DE,#6000
        LD      BC,RES3_E-RES3_B
        LDIR
        RET

;---------------------------------------
;Процедура SAVE_M восстанавливает ранее
;запомненный (с помощью LOAD_M) участок
;памяти.

SAVE_M  LD      HL,#6000
        LD      DE,RES_BUF
        LD      BC,RES3_E-RES3_B
        LDIR
        RET

;---------------------------------------
;Процедура GET_MEM - аналог команды
;LD A,(DE). Нужна для доступа к 0 банку
;памяти.

GET_MEM PUSH    HL
        PUSH    BC
        PUSH    DE
        LD      HL,RES1_B
        CALL    LOAD_R
        POP     DE
        CALL    #6000
        POP     BC
        POP     HL
        RET

;---------------------------------------
;Процедура TO_MEM - аналог команды
;LD (DE),A. Нужна для доступа к 0 банку
;памяти.

TO_MEM  PUSH    HL
        PUSH    BC
        PUSH    DE
        LD      HL,RES2_B
        CALL    LOAD_R
        POP     DE
        CALL    #6000
        POP     BC
        POP     HL
        RET

;---------------------------------------
;Резидент N1 - LD A,(DE):

RES1_B  LD      BC,#7FFD
        LD      HL,(BANK)
        OUT     (C),H
        LD      A,(DE)
        OUT     (C),L
        RET
RES1_E

;---------------------------------------
;Резидент N2 - LD (DE),A:

RES2_B  LD      BC,#7FFD
        LD      HL,(BANK)
        OUT     (C),H
        LD      (DE),A
        OUT     (C),L
        RET
RES2_E

;--------------------------------------
;Резидент N3 определяет, в какой банк
;(1 или 6) загружен модуль art_xy.
;Запоминает это значение в переменной
;BANK.

RES3_B  LD      BC,#7FFD
        LD      A,#11
        OUT     (C),A
        LD      A,(PRIZN)
        CP      "+"
        LD      A,#11
        JR      Z,RES3_1
        LD      A,#16
RES3_1  OUT     (C),A
        LD      (BANK),A
        RET
RES3_E

;---------------------------------------
;Буфер для запоминания участка памяти
;под резидентом:

RES_BUF DS RES3_E-RES3_B

PRIZN   DB "+"
BANK    DW #1000
Страница Ивана Рощина > Статьи >