Демошкола Септика - Урок 1

Демошкола Септика - Урок 1.

автор: Dimouse (перевод)

Введение

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

Я предполагаю, что базовыми знаниями ассемблера вы уже владеете: в этих уроках будут рассказывать именно о программировании демо. Предложенные примеры изначально написаны на AsmOne (однако компилируются и под DevPac - прим. Dimouse).

Регистры

В этом разделе я в каждом уроке буду описывать различные регистры, с которыми нам придется встретиться для достижения поставленной цели. Все они исчерпывающе описаны в Hardware Reference Manual от Commodore, и я рекомендую купить эту книгу, так как это настоящая Библия демо-кодера. Однако, все необходимое будет описано и здесь.

Для начала, наверное, стоит объяснить, что же это за регистры. С помощью чтения или записи в них вы можете обращаться непосредственно к железу Амиги. Коммодор, конечно, утверждает, что обращаться к ним следует через библиотеки и ресурсы, но лучше этого не делать. Часто требуется, например, узнать, где находится луч в строке развертки, и синхронизировать процедуру непосредственно после этого или же поменять цвет фона для каждой строки развертки, создавая таким образом красивейшие эффекты коппера. Все это становится более гладким и, главное, быстрым, если обращаться к регистрам напрямую.

Самый первый и главный регистр, с которого мы начнем, - это DMACON ($dff096). Это так называемый Управляющий DMA Амиги, которым мы можем регулировать, что нам будет нужно, а что нет. Чем этот регистр может управлять:

* DMA приоритета блиттера
* DMA битпланов (таким образом, дает возможность выводить графику)
* DMA коппера
* DMA блиттера
* DMA спрайта
* DMA диска
* DMA звука

Как вы видите, все, что можно себе вообразить в демо, здесь уже есть. Этот регистр управляет тем, что мы видим. Как и большинство других регистров, DMACON

($dff096) - это регистр, открытый ТОЛЬКО НА ЗАПИСЬ, то есть в него можно только записывать. Чтобы прочитать текущее значение DMA, нужно обратиться к дополняющему регистру, DMACONR ($dff002). Регистры, в которых есть интересная информация, обычно имеют дополняющий регистр.

При создании демо следует первым делом продумать заранее, что именно необходимо сделать, как это должно выглядеть и т.д., после этого гораздо проще решить, что записывать в DMA. Посмотрим, как же регистры работают на деле. Этот регистр является регистром-словом (word), т.е., другими словами, содержит 16 битов. Он устроен так же, как почти все другие регистры, а именно:


БИТ       ПОЛОЖЕНИЕ            ОПИСАНИЕ
15        SET/CLR              очищает или устанавливает все биты
14        BBUSY                отображает в работающем или свободном состоянии блиттер
13        BZERO                если установлен, то блиттер закончил работу в положении, где есть только нули
12        X
11        X
10        BLTPRI               приоритет блиттера, соответствует тому, может ли процессор забирать циклы у DMA блиттера
09        DMAEN                позволяет последующие DMA
08        BPLEN                устанавливает DMA битпланов
07        COPEN                устанавливает коппер
06        BLTEN                устанавливает блиттер
05        SPREN                устанавливает DMA спрайтов
04        DSKEN                устанавливает DMA дисков
03        AUD3EN               устанавливает DMA 3 аудио канала
02        AUD3EN               устанавливает DMA 2 аудио канала
01        AUD3EN               устанавливает DMA 1 аудио канала
00        AUD3EN               устанавливает DMA 0 аудио канала


Первым делом в программе необходимо сохранить старые значения DMA (если конечно вы планируете возвращаться в систему) до того, как устанавливать новые. Это легко сделать, просто считав слово из DMACONR ($dff002) и сохранив его до завершения программы. Однако надо установить бит 15 до того, как писать обратно в DMACON ($dff096). Когды мы сохранили старые значения, обычно очищают все биты с помощью записи #$7fff в DMACON ($dff096). Таким образом все биты будут очищены (так как бит 15 будет равен нулю). (все это можно посмотреть в программе Lekt1.s, если что-то непонятно)

После того, как мы изучили DMA, перейдем к копперу. Перед тем, как устанавливать свой коппер-список, так же, как и в случае с DMA, нужно сохранить старые значения адресов (опять же, если вы хотите возвращаться в систему), чтобы можно было их восстановить в конце программы. Это не так тривиально, как в случае с DMA, поскольку для коппер-адресов нет регистра считывания. Можно использовать graphics.library (хотя я этого и не люблю) - открыть graphics.library и считать значение по адресу $26 + gfxlib-running link. После того, как старое значение сохранено, можно спокойно устанавливать свой собственный лист в COP1LC ($dff080).

(Замечание - коппер-список должен быть в CHIP-памяти!) Значение, которое вы туда вводите должно быть длинным словом (long word). После этого вы сможете без проблем записать слово в COPJMP1 ($dff088), чтобы сказать Амиге, что вы используете свой собственный коппер-список. COPJMP1 ($dff088) - это так называемый стробовый регистр, то есть ему без разницы, что в него записано, главное что запись была произведена. Благодаря COPJMP1 ($dff088) и COPJMP2 ($dff08a) вы можете иметь два коппер-списка по адресам COP1LC ($dff080) и COP2LC ($dff084), между которыми можно переключаться.

Теперь перейдем собственно к коппер-спискам. Коппер - это знаменитый сопроцессор, который работает независимо от MC68000 процессора. Коппер - это полезный маленький помощник, незаменимый для амижной графики. С его помощью можно, к примеру, очень просто менять цвета, DMA, триггеры прерываний или даже менять разрешение в любом месте экрана! Поскольку коппер-список работает каждую вертикальную развертку (vertical blanking, vbl), это упрощает процедуру обновления экрана. С помощью коппера можно проверить блиттер или обновить спрайты, а также многое-многое другое!

Его простота заключается в том, что у него всего 3 инструкции: Ожидание (WAIT), Движение (MOVE) и пропуск (SKIP). Инструкции коппера состоят из длинных слов, т.е. двух слов.

Начнем с инструкции WAIT. С ее помощью можно, например, дождаться положения луча в какой-то точке экрана, а затем изменить палитру, или сделать еще что-то забавное.

Описание этой инструкции:

Слово 1:
Бит 0 - всегда установлен в 1 (это, а также 0 во втором слове - идентификаторы инструкции WAIT)
Биты 1-7 - горизонтальное положение, до которого ждать
Биты 8-15 - вертикальное положение, до которого ждать

Слово 2:
Бит 0 - всегда установлен в 0
Биты 1-7 - устанавливает биты, соответствующие горизонтальному положению
Биты 8-14 - устанавливает биты, соответствующие вертикальному положению
Бит 15 - обычно установлен в 1 (см. подробно в Hardware Reference Manual)

Примеры значения в инструкции WAIT:

DC.W $8007,$FF00

Эта команда WAIT будет ждать вертикального ряда $80, в то время как горизонтальное положение $07 будет проигнорировано, так как его биты равны 0 в слове 2 ($ff00).

DC.W $A019,$FFFE

Здесь у нас команда WAIT, в которой горизонтальное положение $19 не будет проигнорировано. Поэтому в данном случае ожидание будет до $A0 по вертикали и $19 по горизонтали, а только потом будет выполнена следующая инструкция. Обратите внимание, что горизонтальное значение должно быть нечетным! Иначе это будет уже команда MOVE. Также это значение должно быть в диапазоне $0 - $e2.

Команда MOVE помещает слово в регистр. Это может быть, к примеру, изменение цвета или обновление битплана. Вот как выглядит команда MOVE:

Слово 1:
Бит 0 - всегда 0
Биты 1-8 - адрес регистра
Биты 9-15 - не используются

Слово 2:
Биты 0-15 - 16 битов, которые будут записаны в регистр


MOVE может записывать во все регистры dff020. Если вы установили бит DANGER коппера ($dff02e), то вы сможете также записывать в регистры от $dff010 до $dff020. Чтобы изменить цвет фона на белый, нужно произвести запись в COLOR00 ($dff180). Если бы мы делали это с помощью процессора, то это выглядело бы так:

MOVE.W #$FFF, $DFF180

Но с коппер-MOVE это будет выглядеть так:

DC.W $0180, $0FFF

Наконец, есть еще команда SKIP. Она редко используется, но все-таки опишем и ее. SKIP похожа на WAIT. В ней также проверяет положение луча, но еще и больше или равно оно положению в следующей инструкции.

Слово 1:
Бит 0 - всегда установлен в 1
Биты 1-7 - горизонтальное положение, до которого ждать
Биты 8-15 - вертикальное положение, до которого ждать

Слово 2:
Бит 0 - всегда установлен в 1
Биты 1-7 - устанавливает биты, соответствующие горизонтальному положению
Биты 8-14 - устанавливает биты, соответствующие вертикальному положению
Бит 15 - обычно установлен в 1 (см. подробно в Hardware Reference Manual)


С помощью SKIP можно, например, отменить строб-запись в COPJMP2, чтобы начать коппер-список номер 2. Если разобраться, как его использовать, то можно делать много классных эффектов. Не буду здесь вдаваться в детали.

Если вы все еще не разобрались, как работает коппер-список, то взгляните на исходники. Там все отлично прокомментировано.

Это все, что я хотел рассказать про регистры в первом уроке. Подробнее, как я уже говорил, все это расписано в Hardware Reference Manual, и вы ДОЛЖНЫ иметь его при себе, если хотите заняться демо-кодингом.

Пример программы

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

Вкратце - программа просто "поднимает" демо-интерфейс, то есть выключает многозадачную систему Амиги и переключается на отдельный экран. Все, что я делаю в процедуре INIT - это следующее: сначала я беру VBR (Vector Base Register). VBR нет на обычных 68000 процессорах, а только на 68010 и выше. Он ссылается на адрес регистров прерывания и исключения вашего компьютера (для 68000 - это начало памяти). VBR нужен для того, чтобы передвинуть их для того, чтобы подключить FAST память. Поскольку прерывание вертикальной развертки находится по адресу $6c, регистр установлен на просмотр адреса относительно $6c. Вы, возможно, думаете, что это слишком мудрено, но это не моя вина. Просто запомните, что VBR нужно достать, чтобы программа заработала.

Следующий шаг - выключить многозадачность. Это обязательно - иначе все процессорные ресурсы уйдут не на вашу замечательную процедуру, а на что-то еще... После этого идет немного нетипичная и временно мной используемая процедура (вы можете ее отключить с помощью переменной DisableCache). Процедура отключает кэш на компьютерах, где это возможно (не на KS1.3 и старше...), зачем же это делать, спросите вы? Ну, кэш может натворить бед при распаковке ресурсов, так что обычно лучше его отключить, если вы не используете его в действительности.

Затем я открываю graphics.library и запускаю OwnBlitter(), чтобы забрать блиттер у системы. После чего WaitBlit() ждет, когда мы его получим. Следующий шаг - Load View (0) - посылает наш коппер-список. Для безопасности мы также ждем WainTOF (дважды). На этот раз нам требуется только очистить Interrupt Control Register, а также DMAn. Теперь перейдем к следующей, еще более странной процедуре - DisableMotor. Неприятно, но если отключить многозадачность, то лампочка занятости жесткого диска может остаться горящей на протяжении всей демки. Эта процедура выключает ее.

После возвращения из функции INIT, я устанавливаю нужный DMA. Для простоты я его записал по вертикали, чтобы можно было легко понять, что есть что. Я устанавливаю только DMA коппера. Цикл работает до тех пор, пока не нажата левая кнопка мыши. После этого запускается функция UnInit. Как вы видите, она делает ровно обратное функции INIT. Сначала мы восстанвливаем старое прерывание и DMA, затем отдаем блиттер обратно системе, а затем и порт экрана и два коппер-списка. Все что осталось - закрыть intuition.library и graphics.library, а затем восстановить кэш. Наконец, можно вернуться к многозадачности.

Много чего еще можно сказать про эту маленькую программу. Внимательно ее изучите и посмотрите, как она работает.

Последнее изменение Mon, 16 Mar 2015 автором Dimouse


Назад в раздел Old-games Diskmag 7